Android架构组件官方文档03——ViewModel

ViewModel概述

ViewModel类旨在以一种有生命周期意识的方式存储和管理与UI相关的数据。
ViewModel类容许数据在配置变化(例如屏幕旋转)后存活。
注意:要将ViewModel导入到Android项目中,请参阅向项目添加组件html

Android framework管理UI控制器的生命周期,例如Activity和Fragment。
framework可能会决定销毁或从新建立UI控制器,以响应彻底不受您控制的特定用户操做或设备事件。android

若是系统销毁或从新建立UI控制器,则存储在其中的任何临时UI相关的数据都将丢失。例如,您的应用可能包含其中一项活动中的用户列表。当为配置更改从新建立活动时,新活动必须从新获取用户列表。
对于简单的数据,Activity可使用onSaveInstanceState()方法并从onCreate()中的bundle中恢复其数据,但此方法仅适用于能够序列化而后反序列化的少许数据,可能不适合像用户或位图的列表这样的大量数据。数据库

另外一个问题是UI控制器常常须要进行异步调用,这可能须要一些时间才能返回。UI控制器须要管理这些调用,并确保系统在销毁后清理它们以免潜在的内存泄漏。这种管理须要大量的维护,而且在为配置更改而从新建立对象的状况下,因为对象可能不得不从新发出已经作出的请求,因此浪费资源。网络

UI控制器(如Activity和Fragment)主要用于显示UI数据,对用户操做作出反应或处理操做系统通讯(如权限请求)。若是要求UI控制器也负责从数据库或网络加载数据,就会使改类变得臃肿。为UI控制器分配过多的责任可能会致使一个类尝试单独处理应用程序的全部工做,而不是将工做委托给其余类。经过这种方式给UI控制器分配过多的责任也使测试变得更加困难。架构

将视图数据全部权从UI控制器逻辑中分离出来更简单,更高效。app

实现一个ViewModel

架构组件为UI控制器提供ViewModel助手类。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<User>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous 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从新建立,它将接收由第一个Activity建立的相同的MyViewModel实例。当持有ViewModel的Activity finish后,框架将调用ViewModel对象的onCleared()方法,以便它能够清理资源。async

警告:ViewModel毫不能引用视图,生命周期或可能持有对活动上下文的引用的任何类。ide

ViewModel对象被设计为脱离视图或LifecycleOwners的特定实例。这种设计还意味着您能够更轻松地编写测试来覆盖ViewModel,由于它不知道视图和生命周期对象。ViewModel对象能够包含LifecycleObservers,例如LiveData对象。可是,ViewModel对象毫不能观察对生命周期感知的可观察对象(如LiveData对象)的更改。若是ViewModel须要应用程序上下文(例如查找系统服务),那么它能够扩展AndroidViewModel类并具备构造函数,该构造函数在构造函数中接收Application,由于Application类扩展了Context。

ViewMode的生命周期

ViewModel对象的范围是在获取ViewModel时传递给ViewModelProvider的生命周期。ViewModel保留在内存中,直到生命周期的范围永久消失:在一个Activity的状况下,finish()时,在一个Fragment的状况下,当它被detached(分离)时。

图1说明了一个Activity在进行一次旋转而后finish后的各类生命周期状态。该图还显示了相关Activity生命周期旁边ViewModel的生命周期。这个特定的图表说明了一个Activity的状态。这些相同的基本状态一样适用于Fragment的生命周期。
图片描述

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

在片断之间共享数据

Activity中的两个或更多fragment须要彼此进行通讯是很常见的。想象一下,主-从关系的F让给met的一种常见状况,其中有一个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.
        });
    }
}

请注意,在获取ViewModelProvider时,这两个Fragment都使用getActivity()。所以,两个Fragment 都接收相同的SharedViewModel实例,该实例的范围限定为Activity。
这种方法具备如下优势:

  • Activity不须要作任何事情,也不须要了解这种沟通。
  • 除了SharedViewModel约定以外,fragment不须要彼此了解。若是其中一个fragment消失,另外一个fragment继续照常工做。
  • 每一个片fragment都有其本身的生命周期,而且不受其余生命周期的影响。若是一个fragment替换另外一个fragment,UI将继续工做而不会出现任何问题。

用ViewModel替换Loaders

CursorLoader这样的Loader类常常用于保持应用程序UI中的数据与数据库同步。您可使用ViewModel和其余几个类来替换Loaders。使用ViewModel将您的UI控制器与数据加载操做分开,这意味着您在类之间的强引用减小了。

在使用loaders的一种常见方法中,应用程序可能使用CursorLoader来观察数据库的内容。当数据库中的值发生更改时,加载程序会自动触发从新加载数据并更新UI:
图片描述
图2.使用加载器加载数据

ViewModel与Room和LiveData一块儿使用来替换Loaders。ViewModel可确保数据在设备配置更改后仍然存在。当数据库发生更改时,Room会通知您的LiveData,而LiveData则会用修改的数据更新您的UI。
图片描述
图3.使用ViewModel加载数据

此博客文章描述了如何将ViewModel与LiveData一块儿使用来替换AsyncTaskLoader

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

Android App Architecture指南建议构建一个存储库类来处理这些功能。

相关文章
相关标签/搜索