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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With