Android DataBinding 正在泄漏内存
Android DataBinding is leaking Memory
我正在使用数据绑定,并且我已经为绑定声明了一个 lateinit var
,当我要去显示泄漏的不同片段 Leaky canary 时。
片段
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
}
...
}
这是来自 Leaky Carny 的信息
androidx.constraintlayout.widget.ConstraintLayout has leaked:
Toast$TN.mNextView
↳ LinearLayout.mContext
↳ MainActivity.navigationView
↳ NavigationView.listener
↳ BaseFragment$setNavigationDrawerItemSelectedListener.this[=12=] (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
我该如何解决这个问题,我是否需要在 onDestroyView
中执行 binding=null
?但是,如果我需要这样做,那么 binding.lifecycleOwner = viewLifecycleOwner
的意义何在?
根据片段 lifecycle,调用了 onDestroyView()
,但片段未完全销毁 - 因此未调用 onDestroy()
。在这种情况下,如果您不手动重置绑定 属性,它会引用 view tree(这是某种泄漏)。
But if I need to do this then what is the point of binding.lifecycleOwner = viewLifecycleOwner then?
如果您向绑定对象提供 LifecycleOwner
,它允许观察生成的绑定 class 中的所有 LiveData
个对象。但它无法了解外部 binding
实例的外部引用(来自您项目的任何其他 classes)——这就是它无法自动重置它们的原因。
这里是google docs推荐的初始化和清除Fragments绑定的方法:
科特林:
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;
}
此外,这里有一个 medium blog post 可以阅读以消除与 属性 委托的绑定内存泄漏。
class FragmentViewBindingDelegate<T : ViewBinding>(
bindingClass: Class<T>,
private val fragment: Fragment
) : ReadOnlyProperty<Fragment, T> {
/**
* initiate variable for binding view
*/
private var binding: T? = null
/**
* get the bind method from View class
*/
private val bindMethod = bindingClass.getMethod("bind", View::class.java)
init {
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
val viewLifecycleOwnerLiveDataObserver =
Observer<LifecycleOwner?> {
val viewLifecycleOwner = it ?: return@Observer
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
binding = null
}
})
}
override fun onCreate(owner: LifecycleOwner) {
fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver)
}
override fun onDestroy(owner: LifecycleOwner) {
fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver)
}
})
}
@Suppress("UNCHECKED_CAST")
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
binding?.let { return it }
/**
* Checking the fragment lifecycle
*/
val lifecycle = fragment.viewLifecycleOwner.lifecycle
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
error("Cannot access view bindings. View lifecycle is ${lifecycle.currentState}!")
}
/**
* Bind layout
*/
val invoke = bindMethod.invoke(null, thisRef.requireView()) as T
return invoke.also { this.binding = it }
}
}
inline fun <reified T : ViewBinding> Fragment.viewBinding() = FragmentViewBindingDelegate(T::class.java, this)
片段内的用法:
private val binding: Fragment_NAME_Binding by viewBinding()
只需在 onDestroyView()
方法中调用 binding.unbind()
。
我正在使用数据绑定,并且我已经为绑定声明了一个 lateinit var
,当我要去显示泄漏的不同片段 Leaky canary 时。
片段
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
}
...
}
这是来自 Leaky Carny 的信息
androidx.constraintlayout.widget.ConstraintLayout has leaked:
Toast$TN.mNextView
↳ LinearLayout.mContext
↳ MainActivity.navigationView
↳ NavigationView.listener
↳ BaseFragment$setNavigationDrawerItemSelectedListener.this[=12=] (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
我该如何解决这个问题,我是否需要在 onDestroyView
中执行 binding=null
?但是,如果我需要这样做,那么 binding.lifecycleOwner = viewLifecycleOwner
的意义何在?
根据片段 lifecycle,调用了 onDestroyView()
,但片段未完全销毁 - 因此未调用 onDestroy()
。在这种情况下,如果您不手动重置绑定 属性,它会引用 view tree(这是某种泄漏)。
But if I need to do this then what is the point of binding.lifecycleOwner = viewLifecycleOwner then?
如果您向绑定对象提供 LifecycleOwner
,它允许观察生成的绑定 class 中的所有 LiveData
个对象。但它无法了解外部 binding
实例的外部引用(来自您项目的任何其他 classes)——这就是它无法自动重置它们的原因。
这里是google docs推荐的初始化和清除Fragments绑定的方法:
科特林:
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;
}
此外,这里有一个 medium blog post 可以阅读以消除与 属性 委托的绑定内存泄漏。
class FragmentViewBindingDelegate<T : ViewBinding>(
bindingClass: Class<T>,
private val fragment: Fragment
) : ReadOnlyProperty<Fragment, T> {
/**
* initiate variable for binding view
*/
private var binding: T? = null
/**
* get the bind method from View class
*/
private val bindMethod = bindingClass.getMethod("bind", View::class.java)
init {
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
val viewLifecycleOwnerLiveDataObserver =
Observer<LifecycleOwner?> {
val viewLifecycleOwner = it ?: return@Observer
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
binding = null
}
})
}
override fun onCreate(owner: LifecycleOwner) {
fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver)
}
override fun onDestroy(owner: LifecycleOwner) {
fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver)
}
})
}
@Suppress("UNCHECKED_CAST")
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
binding?.let { return it }
/**
* Checking the fragment lifecycle
*/
val lifecycle = fragment.viewLifecycleOwner.lifecycle
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
error("Cannot access view bindings. View lifecycle is ${lifecycle.currentState}!")
}
/**
* Bind layout
*/
val invoke = bindMethod.invoke(null, thisRef.requireView()) as T
return invoke.also { this.binding = it }
}
}
inline fun <reified T : ViewBinding> Fragment.viewBinding() = FragmentViewBindingDelegate(T::class.java, this)
片段内的用法:
private val binding: Fragment_NAME_Binding by viewBinding()
只需在 onDestroyView()
方法中调用 binding.unbind()
。