ViewModel用来存储和管理UI相关的数据,可于将一个Activity或Fragment组件相关的数据逻辑抽象出来,并能适配组件的生命周期,如当屏幕旋转Activity重建后,ViewModel中的数据依然有效。
引入ViewModel之前,存在如下几个问题:
onSaveInstanceState()
存储数据,组件重建之后通过onCreate()
,从中读取Bundle恢复数据。但如果是大量数据,不方便序列化及反序列化,则上述方法将不适用。上述几个痛点正是ViewModel出现的原因。
Android架构组件提供了一个ViewModel帮助类来为UI controllers负责数据相关的工作,当配置变化组件销毁重建时,这些数据仍然可以保留。当新的组件重建后,可以立即使用之前保留的数据。下面的示例代码维护了一个User列表数据:
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中可以按如下方式使用ViewModel:
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
或LifecycleOwner
(如Activity实现了LifecycleOwner
接口)要长,因此ViewModel不要引用view
、Lifecycle
或其他引用到Activity上下文的对象。
ViewModel
中可以包含LifecycleObserver
,如LiveData
对象。如果ViewModel
需要使用Application
的上下文对象,则可以通过继承AndroidViewModel
,并提供一个以Application为参数的构造函数。
ViewModel的生命周期依赖于对应的Activity或Fragment的生命周期。通常会在Activity第一次onCreate()
时创建ViewModel,ViewModel的生命周期一直持续到Activity最终销毁或Frament最终detached,期间由于屏幕旋转等配置变化引起的Activity销毁重建并不会导致ViewModel重建。借用官方示意图来解释一下:
上图左侧为Activity的生命周期过程,期间有一个旋转屏幕的操作;右侧则为ViewModel的生命周期过程。
一般通过如下代码初始化ViewModel:
viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);
this
参数一般为Activity或Fragment,因此ViewModelProvider
可以获取组件的生命周期。
Activity在生命周期中可能会触发多次onCreate()
,而ViewModel则只会在第一次onCreate()
时创建,然后直到最后Activity销毁。
日常开发中,一个Activity中可能会有多个Fragment,且他们需要进行交互。例如一个Fragment展示列表,另一个Fragment展示选中列表对应的详情信息,之前我们可能会利用宿主Activity并定义几个接口来实现Fragment之间的交互,另外还得考虑Fragment是否已经创建或显示的问题。
上述痛点,可以使用ViewModel来解决,在Fragment之间可以共享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都用到了如下代码来获取ViewModel:
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
getActivity()
返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个SharedViewModel
对象。
Fragment间共享ViewModel的优点有:
onCreate()
,结束于Activity最终finish时。参考:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html