我在ViewModel中使用了一个Observable字段。当Observable字段更新时,我将更改UI可见性。
这可以通过
完成object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
}
}
删除ondestroy中的回调。
或
使用双向绑定直接像@{}
这样以XML映射。
现在的问题是,如果使用双向绑定,如何删除监听器?我知道Livedata可以替代它。
答案 0 :(得分:1)
我不确定您在谈论哪个内存泄漏。
Java中的内存泄漏发生在一个对象存在很长时间并且它包含对其他对象的强引用,这些强引用不应再使用,因此应该由GC销毁,但由于该强引用而仍然存在。
特别是在Android中,当某些持久对象存储对Activity的强引用(或在某些情况下为Fragment)时,通常会发生内存泄漏。 android中所有其他内存泄漏的影响都不大(带位图的泄漏除外-但这是完全不同的主题)
因此,让我们使用ObservableField
及其在ViewModel
内的回调或通过@={}
进行两种方式的数据绑定返回数据绑定。在大多数情况下,在这两种情况下都不会发生内存泄漏。要了解原因-您将需要了解Android框架如何与UI一起运行,并且还需要了解视图数据绑定的工作原理。因此,当您通过ObservableField
和回调或使用@={}
创建回调时会发生什么
写作时
val someField: ObservabaleField = ObservableFiled<String>("someText")
val someCallback = object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
}
}
someField.addOnPropertyChangedCallback(someCallback)
// and in the layout
android:text="@={viewModel.someField}"
在生成的文件中,它会执行类似的操作
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, viewModelSomeFieldGet);
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
//...
return onChangeViewModelSomeOtherStuff(object, fieldId);
case 1 :
return onChangeViewModelSomeField((androidx.databinding.ObservableField<java.lang.String>) object, fieldId);
}
return false;
}
如您所见,context
或activity
都没有fragment
泄漏,因为没有强烈引用将它们存储在任何地方。您的context
中也没有引用activity
,fragment
或ViewModel
(希望!)。此外,它以另一种方式起作用-ui存储在绑定实现中链接到ViewModel
,因此我们的ViewModel
可能会泄漏。由于活动或片段的UI通常会连同其ActivityBindingImpl
或FragmentBindingImpl
绑定一起被销毁,所以...
为确保您有清除引用的手动方法:在Activity'onDestroy
或Fragment'onDestroyView
调用中
clearFindViewByIdCache()
binding.unbind()
binding = null
// if you store view link in your viewModel(which is bad and may cause leaks) this is the perfect place to nullify it
viewModel.view = null
还可以使用AutoClearedValue
实际用法可能看起来像(如果您不关心它的类型)
override var binding: ViewDataBinding? by autoCleared()// that is all - no need of onDestroy or onDestroyView
修改
如果您要手动注销ObservableField
中的所有回调,则可以这样做。最好的方法是使用onCleared()
的{{1}}方法。您应该致电ViewModel
处理这些东西。考虑到上面的observableField.removeOnPropertyChangedCallback(callback)
和回调声明,它看起来像这样:
ObservableField
编辑结束
以上所述,所有这些确保了在使用class MyViewModel: ViewModel{
//ObservableField and callback declarations
...
override void onCleared(){
someField.removeOnPropertyChangedCallback(someCallback)
}
}
并查看数据绑定时不会发生内存泄漏。这都是关于正确的实现。当然,您可以使用泄漏实现它,但是您可以不使用泄漏来实现它。
如果仍然不清楚,请发表评论-我将尝试扩大答案。
有关依赖片段的泄漏here
的更多信息希望有帮助。
答案 1 :(得分:1)
您可以使用ViewModel类中的removeOnPropertyChangedCallback
函数来实现。这是您的ViewModel的样子:
abstract class ObservableViewModel(app: Application): AndroidViewModel(app), Observable {
@delegate:Transient
private val mCallBacks: PropertyChangeRegistry by lazy { PropertyChangeRegistry() }
override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) {
mCallBacks.add(callback)
}
override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) {
mCallBacks.remove(callback)
}
fun notifyChange() {
mCallBacks.notifyChange(this, 0)
}
fun notifyChange(viewId:Int){
mCallBacks.notifyChange(this, viewId)
}
}
答案 2 :(得分:1)
removeOnPropertyChangedCallback永远不会被调用?
这实际上是 最终并定期被数据绑定框架调用 清理已收集的听众。但是,很可能 您的ViewModel仍然会注册一些回调 毁了,这没关系。数据绑定框架使用弱 给观察者的参考资料,并不一定要 在销毁ViewModel之前取消注册。这不会造成任何 内存泄漏。
这样说,如果您快速旋转手机, 行,而在同一屏幕上。您会注意到 ObservableViewModel.addOnPropertyChangedCallBack被称为几个 时间,如果您查看内部来源 android.databinding.ViewDataBinding,您将看到观察者计数 每次都会上升。
这是定期删除的地方。如果您长时间使用该应用程序 足够,旋转几次,并在其上设置一个断点 ObservableViewModel.removeOnPropertyChangedCallback。您会看到 定期调用它来清理旧的观察者,如果您查找 您可以在调用堆栈中找到有关其来源的更多详细信息, 是如何触发的,等等。
您可以在https://caster.io/lessons/android-mvvm-pattern-with-architecture-component-viewmodels上进行跟踪。
希望这对您有帮助!