具有双向绑定的可观察字段用法(删除 属性 更改侦听器)

Observable field usage with two way binding (Removing property change listener)

我在 ViewModel 中使用 Observable 字段。当 Observable 字段更新时,我更改 UI 可见性。

这可以通过

完成
object : Observable.OnPropertyChangedCallback() {
                override fun onPropertyChanged(sender: Observable?, propertyId: Int) {

                }

            }

移除 ondestroy 中的回调。

直接在 XML 中映射,就像 @{} 使用双向绑定。

现在的问题是,如果使用双向绑定,我该如何删除侦听器?我知道 Livedata 可以替代它。

我不确定你说的是哪种内存泄漏。

Java 中的内存泄漏发生在一个对象存在很长时间并且它包含对不应再使用的其他对象的强引用时,因此应该被 GC 销毁,但仍然存在因此强参考。

在 Android 中,内存泄漏通常发生在某些持久对象存储对 Activity(或在某些情况下为片段)的强引用时。 android 中的所有其他内存泄漏都没有那么有影响(除了带有位图的内存泄漏 - 但这是一个完全不同的主题)

所以让我们return使用 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 activityfragment 泄漏,因为没有对它们的强引用存储在任何地方。在您的 ViewModel 中也没有对 contextactivityfragment 的引用(我希望!)。此外,它以相反的方式工作——ui 将 link 存储到绑定实现中的 ViewModel,因此我们的 ViewModel 可能会泄漏。这是后置情况,因为 Activity 或片段的 UI 通常会与其 ActivityBindingImplFragmentBindingImpl 绑定一起被销毁,但是...

确保您有手动清除引用的方法:在 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 的所有回调,您可以这样做。最好的方法是在 ViewModelonCleared() 方法中。你应该打电话给 observableField.removeOnPropertyChangedCallback(callback) 来处理这些事情。考虑到 ObservableField 和上面的回调声明,它看起来像这样:

class MyViewModel: ViewModel{
   //ObservableField and callback declarations
   ...
   override void onCleared(){
       someField.removeOnPropertyChangedCallback(someCallback)
   }

}

编辑结束

我刚才描述的所有这些确保在使用 ObservableFields 和视图数据绑定时不会出现内存泄漏。这完全取决于正确的实施。当然你可以用leaks来实现,但是你也可以不用leaks来实现。

如果仍有不清楚的地方,请发表评论 - 我会尝试扩展答案。

关于片段相关泄漏的更多信息here

希望对您有所帮助。

您可以使用 ViewModel class 中的 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)
}
}

removeOnPropertyChangedCallback never gets called?

This actually does get called, eventually and periodically, by the Data Binding framework to clean listeners that have been collected. It’s likely however, that your ViewModel will still have some callbacks registered when it is destroyed, and this is okay. The Data Binding framework uses weak references for the observers and it’s not required that they be unregistered before the ViewModel is destroyed. This won’t cause any memory leaks.

With that said, if you rotate the phone rapidly, several times in a row, while on the same screen. You’ll notice ObservableViewModel.addOnPropertyChangedCallBack is called several times and if you look inside the source for android.databinding.ViewDataBinding, you’ll see the observer count does rise each time.

This is where the periodic removal comes in. If you use the app long enough, rotate a few times, and have a breakpoint set on ObservableViewModel.removeOnPropertyChangedCallback. You’ll see that it is called periodically to clean up old observers and if you look up the call stack you can find more detail about where that comes from, how it’s triggered, etc.

您可以在以下位置跟踪更多信息:https://caster.io/lessons/android-mvvm-pattern-with-architecture-component-viewmodels

希望对您有所帮助!!