ViewModel [译]

ViewModel

ViewModel被设计用来存储并管理UI相关的数据。
ViewModel允许数据在配置进行更改时,不被销毁。如:屏幕旋转。
注意:要导入 ViewModel到您的Android项目中,请参阅向项目添加组件

Android framwork管理UI controllers的生命周期,比如activity和fragment。
framework可能销毁或重建一个UI controller以响应完全不受控制的某些用户操作或设备事件。

如果系统销毁或重新创建UI controller,则存储在其中的所有瞬态UI相关数据都将丢失。例如,您的应用可能会在其中一个Activity中包含用户列表。当针对配置更改重新创建Activity时,新Activity必须重新获取用户列表。对于简单的数据,Activity 可以使用该 onSaveInstanceState()方法并从数据包中恢复其数据 onCreate(),但是这种方法仅适用于可以序列化,然后反序列化的少量数据,而不适用于大量数据,如用户列表或位图。

另一个问题是UI controller 经常需要进行异步调用,这可能需要一些时间才能返回。UI controller需要管理这些调用,并确保系统在销毁后清理它们,以避免潜在的内存泄漏。这种管理需要大量的维护,并且在为配置更改而重新创建对象的情况下,由于对象可能不得不重新发出已经做出的调用,所以浪费资源。

UI controller(如activities和fragments)主要用于显示UI数据,对用户操作做出反应,或处理操作系统通信(如权限请求)。要求UI controller 也负责从数据库或网络加载数据,从而增加了该类的膨胀。为UIcontroller分配过多的责任可能会导致一个类尝试单独处理应用程序的所有工作,而不是将工作委托给其他类。通过这种方式给UI控制器分配过多的责任也使测试变得更加困难。

将View数据所有权从UI controller逻辑中分离出来更简单,更高效。

实现一个ViewModel

架构组件(Architecture Components )为ViewModel负责为UI准备数据的UI controller 提供 助手类。 ViewModel对象在配置更改期间会自动保留,以便它们保存的数据立即可用于下一个Activity或fragment实例。例如,如果您需要在应用程序中显示用户列表,请确保分配责任获取并将用户列表保留为 ViewModel,而不是activity 或 fragment,如以下示例代码所示:

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<Users>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asyncronous operation to fetch users.
    }
}

然后,您可以按如下方式访问activity 列表:

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

如果activity 被重新创建,它将接收MyViewModel到第一个activity 创建的同一个实例。所有者activity 完成后,框架将调用 ViewModel对象的 onCleared() 方法,以便清理资源。
注意:一个ViewModel 决不能引用view, Lifecycle或任何可持有activity context的引用的类。

ViewModel对象被设计为比Views或特定的实例更具活力 LifecycleOwners。这个设计也意味着你可以编写测试来ViewModel更容易地覆盖, 因为它不知道Views和 Lifecycle对象。 ViewModel 对象可以包含 LifecycleObservers,比如 LiveData对象。但是, ViewModel对象绝不能观察生命周期感知的可观察LiveData对象(如对象)的更改。如果 ViewModel需要 Application Context,例如找到一个系统服务,它可以扩展 AndroidViewModel 类,并有一个构造函数接收Application 构造函数,因为Application类继承Context。

ViewModel的生命周期

ViewModel对象的范围是Lifecycle传递给 ViewModelProvider 获取的时候 ViewModel。ViewModel记忆中的 遗体,直到Lifecycle 永久消失为止:在一个Activity的情况下,当它finishes时,在一个fragment的情况下,当它被detached时。

图1展示了一个Activity在经历一个循环后的各种生命周期状态,然后结束。该图还显示ViewModel了关联activity 生命周期的。这个特定的图表说明了一个activity 的状态。相同的基本状态适用于fragment的生命周期。

您通常ViewModel首次请求 系统调用activity 对象的 onCreate()方法。系统可能会onCreate()在整个activity 的整个生命周期中多次调用 ,例如当设备屏幕旋转时。在 ViewModel存在从当你第一次请求 ViewModel,直到activity 结束和销毁。

在Fragment之间共享数据

一个Activity中的两个或多个fragment需要相互通信是很常见的。想象一下,一个常见的master-detail fragment的情况下,你有一个fragment,其中用户从列表中选择一个项目,另一个fragment显示所选项目的内容。这种情况从来都不是微不足道的,因为这两个fragment都需要定义一些接口描述,而所有者Activity必须将两者联系在一起。此外,这两个fragment必须处理其他fragment尚未创建或可见的场景。

这个常见的痛点可以通过使用ViewModel对象来解决 。这些fragment可以使用它们的Activity范围内共享一个 ViewModel来处理此通信,如以下示例代码所示:

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}

public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

请注意,这两个fragment使用 getActivity()时得到的 ViewModelProvider。结果,这两个fragment都接收到相同的SharedViewModel实例,这个实例被限制在Activity范围内。

这种方法提供了以下好处:

这个Activity不需要做任何事情,也不需要知道这个交流。
除SharedViewModel 合同之外,fragment不需要彼此了解。如果其中一个fragment消失,另一个fragment继续照常工作。
每个fragment都有自己的生命周期,不受其他生命周期的影响。如果一个fragment替换另一个fragment,则UI继续工作而没有任何问题

用ViewModel替换Loaders

Loader类CursorLoader经常用来保持应用程序的UI中的数据与数据库同步。您可以使用 ViewModel其他几个类来替换加载器。使用 ViewModel分离你的UI控制器从数据加载操作,这意味着你有更少的强类之间的引用。

在使用加载程序的一种常见方法中,应用程序可以使用a CursorLoader来观察数据库的内容。当数据库中的值发生更改时,加载器会自动触发重新加载数据并更新UI:

图2.使用加载器加载数据
ViewModel与 Room和 LiveData一起使用来替换加载器。在ViewModel该数据存续的设备配置改变确保。 当数据库发生变化时,会议室会通知您 LiveData,而LiveData又会使用修改后的数据更新您的用户界面。

图3.使用ViewModel加载数据
这篇博客文章介绍了如何ViewModel用a LiveData来替换一个 AsyncTaskLoader。

随着你的数据变得越来越复杂,你可能会选择一个单独的类来加载数据。其目的 ViewModel是封装UI Controller的数据,以使数据在配置更改后不受影响。有关如何通过配置更改加载,保持和管理数据的信息,请参阅 保存UI状态。

在对Android应用程序架构指南建议建立一个资料库类来处理这些功能。

参考:
原文链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值