android - 如果我清理反向引用,我可以观察 ViewModel 吗?

标签 android observer-pattern android-viewmodel

实现ViewModel 的建议方法是使用LiveData 对象向 Activity 、 fragment 和 View 公开不断变化的数据。在某些情况下,LiveData 不是理想的答案或根本没有答案。

自然的替代方案是,将观察者模式应用于 ViewModel,使其成为可观察对象。当向 ViewModel 注册观察者时,ViewModel 将保存回调引用以通知观察者。

文档说,ViewModel 不得包含对 Activity 、 fragment 或 View 的引用。我发现“为什么”这个问题的唯一答案是,这可能会导致内存泄漏。那么如何清理引用以避免内存泄漏呢?

对于 View 来说,这是一个困难。当 View 消失时,没有定义的时刻。但是 Activity 和 Fragments 有一个定义的生命周期。所以有一些地方可以注销观察员。

你怎么看?如果您注意始终取消注册,将 Activity 注册为 ViewModels 的观察者是否有效?关于这个问题,您是否发现了任何有效信息?

我为最佳答案设置了小额奖励。这不是因为我认为它是推荐的解决方案(因为它不适用于 View )。我只想知道并扩展我的选择。

public class ExampleViewModel extends ViewModel {

    public interface OnEndListener {
        public void onEnd();
    }

    private List<OnEndListener> onEndListeners = new ArrayList<>();

    public void setOnEndListener(OnEndListener onEndListener) {
        onEndListeners.add(onEndListener);
    }

    public void removeOnEndListener(OnEndListener onEndListener) {
        onEndListeners.remove(onEndListener);
    }

    public void somethingHappens() {
        for (OnEndListener onEndListener: new ArrayList<OnEndListener>(onEndListeners) ) {
            onEndListener.onEnd();
        }
    }
}

public class ExampleActivity extends AppCompatActivity {

    ExampleViewModel exampleViewModel;
    ExampleViewModel.OnEndListener onEndListener;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        onEndListener = new ExampleViewModel.OnEndListener() {
            @Override
            public void onEnd() {
                finish();
            }
        };
        exampleViewModel = ViewModelProviders.of(this).get(ExampleViewModel.class);
        exampleViewModel.setOnEndListener(onEndListener);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        exampleViewModel.removeOnEndListener(onEndListener);
    }
}

最佳答案

在我看来,问“我可以……”并不是一个真正有用的问题。文档清楚地表明不鼓励您提出的建议以及原因。也就是说,我希望您的代码可能会按预期工作,因此是“允许的”(即不受技术限制阻止)。

一个可能的陷阱场景:ExampleActivity 的 InstanceA 已启动,并在 ExampleViewModel 上启动了一些长时间运行的任务。然后,在任务完成之前,设备被轮换并且 InstanceA 由于配置更改而被销毁。然后,在销毁 InstanceA 和创建新的 InstanceB 之间,长时间运行的任务完成,您的 View 模型调用 onEndListener.onEnd()。除了:哦,不! onEndListenernull 因为它在 InstanceA 被销毁时被清除并且尚未被 InstanceB 设置:NullPointerException

ViewModel 的设计(部分)恰好 是为了处理边缘情况,例如上面的陷阱场景。因此,与其违背 ViewModel 的预期用途,为什么不使用它提供的工具以及 LiveData 来完成同样的事情呢? (我可能会添加更少的代码。)

public class ExampleActivity extends AppCompatActivity {

    ExampleViewModel exampleViewModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        exampleViewModel = ViewModelProviders.of(this).get(ExampleViewModel.class);
        exampleViewModel.getOnEndLive().observe(this, new Observer<Boolean>() {
            @Override
            public void onChanged(@Nullable Boolean onEnd) {
                if (onEnd != null && onEnd) {
                    finish();
                }
            }
        });

    }
}

public class ExampleViewModel extends ViewModel {

    private MutableLiveData<Boolean> onEndLive = new MutableLiveData<>();

    public MutableLiveData<Boolean> getOnEndLive() {
        return onEndLive;
    }

    public void somethingHappens() {
        onEndLive.setValue(true);
    }
}

在这种情况下,不要将 LiveData 视为实际的“数据”本身,而是将其视为可以从 ViewModel 传递到 Activity 的信号。我一直使用这种模式。

关于android - 如果我清理反向引用,我可以观察 ViewModel 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50000975/

相关文章:

菜单选项中的android显示对话框不起作用

android - OBJ vs 3DS - Android 3D 开发的最佳格式是什么

javascript - JavaScript 代理的替代品

Java Inter Application Form通信观察者模式

android - 这是 LiveData 的正确使用方式吗?

android - 在 MVVM 中处理 ViewModel 和 Fragment 中的数据

android - Fleetboard 上的 TLS 1.2

java - httpget 使用相同的应用程序和 PHP 文件从本地和互联网网络服务器获得不同的响应

oop - 为什么观察者设计模式通常被描绘成一对多?

android - 奇怪的 AndroidViewModel LiveData 观察者行为