Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android DataBinding is leaking Memory

Tags:

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 inside onDestroyView? But if I need to do this then what is the point of binding.lifecycleOwner = viewLifecycleOwner then?

like image 292
OhhhThatVarun Avatar asked Aug 25 '19 16:08

OhhhThatVarun


2 Answers

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.

like image 172
Eren Avatar answered Sep 27 '22 20:09

Eren


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.

like image 38
ConstOrVar Avatar answered Sep 27 '22 21:09

ConstOrVar