Android DataBinding is leaking Memory
Solution 1
According to fragment lifecycle, onDestroyView()
was called, but fragment is not fully destroyed - so onDestroy()
is not called. In that case, if you don't reset binding property by hand, it references view tree (it's some kind of leak).
But if I need to do this then what is the point of binding.lifecycleOwner = viewLifecycleOwner then?
If you supply LifecycleOwner
to binding object, it allows to observe all LiveData
objects inside generated binding class. But it can't know about external references on binding
instance outside (from any other classes of your project) - that's why it can't automatically reset them.
Solution 2
Here is the recommended way from google docs to initialize and clear the binding in Fragments:
Kotlin:
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
Java:
private ResultProfileBinding binding;
@Override
public View onCreateView (LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
binding = ResultProfileBinding.inflate(inflater, container, false);
View view = binding.getRoot();
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
Also, here is a medium blog post to read to get rid of binding memory leak with Property Delegate.
Solution 3
Just call binding.unbind()
in onDestroyView()
method.
![OhhhThatVarun](https://i.stack.imgur.com/8My3C.jpg?s=256&g=1)
Comments
-
OhhhThatVarun about 2 years
I'm using data-binding and I have declared a
lateinit var
for the binding and when I'm going to different fragment Leaky canary showing a leak.Fragment
class HomeFragment : BottomNavViewHostBaseFragment() { private lateinit var viewModel: HomeViewModel private lateinit var binding: FragmentHomeBinding override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java) binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false) binding.lifecycleOwner = viewLifecycleOwner binding.viewModel = viewModel return binding.root } ... }
Here is the Info from Leaky Carny
androidx.constraintlayout.widget.ConstraintLayout has leaked: Toast$TN.mNextView ↳ LinearLayout.mContext ↳ MainActivity.navigationView ↳ NavigationView.listener ↳ BaseFragment$setNavigationDrawerItemSelectedListener$1.this$0 (anonymous implementation of com.google.android.material.navigation.NavigationView$OnNavigationItemSelectedListener) ↳ OrdersHostFragment.mFragmentManager ↳ FragmentManagerImpl.mActive ↳ HashMap.table ↳ array HashMap$HashMapEntry[].[0] ↳ HashMap$HashMapEntry.value ↳ HomeFragment.!(binding)! ↳ FragmentHomeBindingImpl.!(mboundView0)! ↳ ConstraintLayout
How do I solve this and Should I need to do
binding=null
insideonDestroyView
? But if I need to do this then what is the point ofbinding.lifecycleOwner = viewLifecycleOwner
then?-
Agent_L almost 3 yearsWhy are you holding the binding? What is it useful to? Genuine question, I've always used the binding as
onCreateView
local variable. -
OhhhThatVarun almost 3 years@Agent_L There could be multiple
Views
that I need to access outside ofonCreateView
to perform actions. LikeVisibility
of a button etc. -
Agent_L almost 3 yearsFor me, the whole point of DataBinding is to never have to access
View
s again. -
OhhhThatVarun almost 3 years@Agent_L how? Do you do all the manipulations via
ViewModel
andBinding Adapters
? -
Agent_L almost 3 yearsYeah, that's the point. No more code in
Fragment
/Activity
.
-
-
OhhhThatVarun almost 5 yearsWhat should I do for solving it?
binding = null
inonDestroyView
? -
beigirad over 4 yearsif referencing to a view is kind of leak, for example, an instance of TextView in my fragment is leak too? so traditional usage that defined all of the view in the top of fragment/activity is a memory-leak. so how should we access to views is OK?
-
ConstOrVar over 4 years@beigirad, referencing to view is not necessarily a leak. For example, if you have activity and save view in variable - it's not memory leak, GC resolves that after activity is destoryed. If you have fragment, you can use view only between
onCreateView
andonDestroyView
calls. If you can access view, afteronDestroyView
was called - it's problem. That's also actual for binding's instances. -
Mohsen Einhesari over 4 years@VarunRaj did you solve your problem? by making binding = null in onDestroyView ? I only found this solution working but I don't think it's a good approach.
-
ConstOrVar over 4 years@MohsenEinhesari, if you save binding in some variable in fragment, it's absolutely normal to null it by hand (as you need to do for each view stored in variable), because lifetime of fragment's view is less than lifetime of fragment.
-
Mohsen Einhesari over 4 years@ConstOrVar I think in most cases we should store binding in a variable, right? and another thing, what does binding.unbind do? I thought it should avoid memory leak too ( like binding = null).
-
ConstOrVar over 4 years@MohsenEinhesari, store binding in variables is not always required - so it depends on screen's logic.
unbind()
according to documentation simply removes listeners, but still have view's cached. -
OhhhThatVarun over 4 years@MohsenEinhesari yeah it kinda did solve my problem. Then what should I do then?
-
Mohsen Einhesari over 4 years@VarunRaj nothing! I was looking for a better solution but after ConstOrVar comments I think it's good enough.
-
Daniel Wilson almost 4 yearsNote that link refers to ViewBinding, but I assume the concept is the same with DataBinding even though I can't find a doc that says so and the samples don't nullify view references either.
-
Aorlinn almost 3 yearsI cannot see any unbind method in my binding.
-
Aorlinn almost 3 yearssorry my fault, I was using ViewBinding instead of DataBinding
-
hsm59 almost 3 yearsCan we use
WeakReference<FragmentClassDataBinding>
for this, and not worry about the unbinding / setting binding = null inonDestroyView()
? -
hsm59 almost 3 yearsCan we use WeakReference<FragmentClassDataBinding> for this, and not worry about the unbinding / setting binding = null in onDestroyView() ? ConstOrVar, @Mohsen Einhesari