在Model - build.gradle添加依赖
implementation "android.arch.lifecycle:viewmodel:1.1.1"
该ViewModel
课程旨在存储和管理用户界面相关的数据生命周期中的意识的方式。在 ViewModel
类允许数据生存如屏幕旋转配置更改。
注意:要导入 ViewModel
到您的Android项目中,请参阅向项目添加组件。
Android框架管理UI控制器的生命周期,例如活动和片段。框架可能会决定销毁或重新创建UI控制器,以响应完全不受您控制的特定用户操作或设备事件。
如果系统销毁或重新创建UI控制器,则存储在其中的任何瞬态UI相关数据都将丢失。例如,您的应用可能包含其中一项活动中的用户列表。当为配置更改重新创建活动时,新活动必须重新获取用户列表。对于简单的数据,活动可以使用该 onSaveInstanceState()
方法并从数据包中恢复其数据 onCreate()
,但是此方法仅适用于可以序列化然后反序列化的少量数据,而不适用于潜在的大量数据,如用户列表或位图。
另一个问题是UI控制器经常需要进行异步调用,这可能需要一些时间才能返回。UI控制器需要管理这些调用,并确保系统在销毁后清理它们以避免潜在的内存泄漏。此管理需要大量维护,并且在为配置更改重新创建对象的情况下,由于对象可能不得不重新发出已经创建的呼叫,因此这会浪费资源。
UI控制器(如活动和片段)主要用于显示UI数据,对用户操作做出反应或处理操作系统通信(如权限请求)。要求UI控制器也负责从数据库或网络加载数据,从而增加了该类的膨胀。为UI控制器分配过多的责任可能会导致一个类尝试单独处理应用程序的所有工作,而不是将工作委托给其他类。通过这种方式给UI控制器分配过多的责任也使测试变得更加困难。
将视图数据所有权从UI控制器逻辑中分离出来更简单,更高效。
实现一个ViewModel
架构组件为ViewModel
负责为UI准备数据的UI控制器提供 助手类。 ViewModel
对象在配置更改期间会自动保留,以便它们保存的数据立即可用于下一个活动或片段实例。例如,如果您需要在应用中显示用户列表,请确保分配责任获取并将用户列表保留为 ViewMode
l,而不是活动或片段,如以下示例代码所示:
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 asynchronous operation to fetch users.
}
然后你可以从一个活动中访问列表,如下所示:
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
});
}
}
如果活动被重新创建,它会收到MyViewModel
第一个活动创建的同一个实例。所有者活动完成后,框架会调用 ViewModel
对象的 onCleared()
方法,以便清理资源。
注意:一ViewModel
决不能引用视图, Lifecycle
或可保持到活动上下文的引用的任何类。
ViewModel
对象被设计为超过视图或特定实例 LifecycleOwners
。这个设计也意味着你可以编写测试来ViewModel
更容易地覆盖, 因为它不知道视图和Lifecycle
对象。 ViewModel
对象可以包含 LifecycleObservers
,比如 LiveData
对象。但是, ViewModel
对象绝不能观察对生命周期感知的可观察LiveData
对象(如对象)的更改。如果 ViewModel
需要 Application
上下文,例如查找系统服务,则可以扩展 AndroidViewModel
该类并在构造函数中接收Application
构造函数,因为Application
类会扩展Context
。
ViewModel的生命周期
ViewModel
对象被限定在Lifecycle
传递给 ViewModelProvider
获得时 ViewModel
。ViewModel
内存中的 遗留物,直到Lifecycle
它的作用域永久消失:在一个活动的情况下,当它结束时,在一个碎片的情况下,当它被分离时。
图1说明了一个活动在进行一次旋转并完成后的各种生命周期状态。该图还显示 ViewModel
了关联活动生命周期旁边的生命周期。这个特定的图表说明了一个活动的状态。相同的基本状态适用于片段的生命周期。
您通常ViewModel
首次请求 系统调用活动对象的 onCreate()
方法。系统可能会onCreate()
在整个活动的整个生命周期中调用 多次,例如当设备屏幕旋转时。在 ViewModel
存在从当你第一次请求 ViewModel
,直到活动结束和销毁。
在片段之间共享数据
活动中的两个或更多片段需要彼此进行通信是很常见的。想象一下,主 - 细节片段的一种常见情况,其中有一个片段,用户从列表中选择一个项目,另一个片段显示所选项目的内容。这种情况从来都不是微不足道的,因为这两个片段都需要定义一些接口描述,并且所有者活动必须将两者绑定在一起。此外,这两个片段必须处理其他片段尚未创建或可见的场景。
这个常见的痛点可以通过使用ViewModel
对象来解决 。这些片段可以使用其活动范围共享一个 ViewMode
l来处理此通信,如以下示例代码所示:
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.
});
}
}
注意这两个片段 getActivity()
在获取时使用 ViewModelProvider
。因此,这两个片段都会收到相同的SharedViewModel
实例,这个实例的作用域是活动范围。
这种方法提供了以下好处:
- 这项活动不需要做任何事情,也不需要了解任何有关此沟通的信息。
- 除
SharedViewModel
合同外,碎片不需要了解彼此。如果其中一个片段消失,另一个片断继续照常工作。 - 每个片段都有其自己的生命周期,并且不受其他生命周期的影响。如果一个片段替换另一个片段,UI将继续工作而不会出现任何问题。
用ViewModel取代装载机
Loader类CursorLoader
经常用于保持应用程序UI中的数据与数据库同步。您可以使用 ViewModel
其他几个类来替换加载器。使用a ViewModel
将UI控制器与数据加载操作分开,这意味着您在类之间的强引用减少了。
在使用加载程序的一种常见方法中,应用程序可能会使用a CursorLoader
来观察数据库的内容。当数据库中的值发生更改时,加载器会自动触发重新加载数据并更新UI:
ViewModel
与 Room和 LiveData一起使用来替换加载器。在ViewModel
该数据存续的设备配置改变确保。 会议室会通知您 LiveData
数据库更改的时间,而LiveData则会用修改后的数据更新您的用户界面。
本博客文章介绍了如何使用ViewModel
with和a LiveData
来替换 AsyncTaskLoader
。
随着你的数据变得越来越复杂,你可能会选择一个单独的类来加载数据。其目的 ViewModel
是封装UI控制器的数据以使数据在配置更改后不受影响。有关如何通过配置更改加载,保持和管理数据的信息,请参阅 保存UI状态。
在对Android应用程序架构指南建议建立一个资料库类来处理这些功能。
网友评论