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

Roc*_*kin 5 android android-databinding android-architecture-components

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

这可以通过

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

                }

            }
Run Code Online (Sandbox Code Playgroud)

删除 ondestroy 中的回调。

或者

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

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

Pav*_*sha 2

我不确定你在谈论哪种内存泄漏。

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

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

因此,让我们回到通过 的数据绑定及其在双向数据绑定ObservableField中的回调。在大多数情况下,这两种情况都不会发生内存泄漏。要了解原因 - 您需要了解 Android 框架如何使用 UI 进行操作,并且还需要了解视图数据绑定现在是否有效。那么,当您通过and 回调或 with创建回调时会发生什么ViewModel@={}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}"
Run Code Online (Sandbox Code Playgroud)

在生成的文件中它做了类似的事情

    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;
    }

Run Code Online (Sandbox Code Playgroud)

正如您所看到的,不存在任何context泄漏activityfragment泄漏,因为没有对它们存储在任何地方的强引用。您的任何一个中都没有提到context,activity或(我希望!)。此外,它的工作方式相反 - ui 存储到绑定实现中的链接,因此我们可能会泄漏。这是后例,因为 Activity 或 Fragment 的 UI 通常会与其绑定一起被破坏,但是......fragmentViewModelViewModelViewModelActivityBindingImplFragmentBindingImpl

确保您有手动方式清除引用:在“活动”onDestroy或“片段”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
Run Code Online (Sandbox Code Playgroud)

另外,为了处理绑定自动清除,您可以使用AutoClearedValue

实际用法可能看起来像(如果你不关心它的类型)

override var binding: ViewDataBinding? by autoCleared()// that is all - no need of onDestroy or onDestroyView
Run Code Online (Sandbox Code Playgroud)

编辑

如果您想手动取消注册 s 中的所有回调,ObservableField您可以这样做。最好的方法是使用onCleared()的方法ViewModel。你应该打电话observableField.removeOnPropertyChangedCallback(callback)来处理这些事情。ObservableField考虑到上面的回调声明,它看起来像这样:

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

}
Run Code Online (Sandbox Code Playgroud)

编辑结束

我刚刚描述的所有内容可确保在使用ObservableFields和查看数据绑定时不会出现内存泄漏。这一切都与正确的实施有关。当然,你可以在有泄漏的情况下实现它,但你也可以在没有泄漏的情况下实现它。

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

有关片段相关泄漏的更多信息请参见此处

希望能帮助到你。