本文将介绍Architecture Components中的ViewModel组件

ViewModel

The ViewModel 用来管理和存储UI相关的数据,和普通的data store 不同的是:它能在 configuration changes 后,保持存活.

ViewModel主要解决了以下问题:

  1. activities 和 fragments组件,随时可能会被系统 destroy 或者 recreate 。但是存储在组件中的数据状态却可能因此丢失,onSaveInstanceState()是一种解决办法,但是这个方法的本意是用来存储并还原UI,并不适合存储大量数据。

  2. 我们经常在组件中执行一些异步操作,有一个常见的新手 crash 就是在异步回调中没有检查组件生命周期就直接去访问UI。异步回调另一个问题是内存泄露。避免这些bug很简单,在 UI controller destroy时,清理掉异步回调。但是为啥不能让UI自动清理,而且,recreate后,我们得重新加载这些异步操作。

  3. UI controllers 本来就需要处理用户交互。现在还得自己处理数据资源了。大部分的Activity最后都变成了"god activities"。测试这些类只会让人头疼。

show me the code

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 async operation to fetch users
}
}

现在 activity 中不再直接持有 user list :

public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
  1. Activity 重建后,会得到同一个 MyViewModel 实例。
  2. Activity 销毁后, ViewModel.onCleared()会自动清理。
  3. ViewModel不应该持有任何View,或者Activity context,如果需要Application context,可以让ViewModel继承AndroidViewModel

在 Fragments 之间共享数据

现在可以通过同一个activity 的 ViewModel来共享数据。不用再抽象fragment interface、bind 到activity、处理fragment生命周期。。。

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 LifecycleFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// update UI
});
}
}

上述代码中的两个
fragments都通过

ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

获取了同一个基于getActivity 的ViewModel 实例。

优点:

  • activity不用再操心fragment之间的数据共享了。
  • Fragments 也不需要知晓除了SharedViewModel之外的内部的实现细节,即使其中一个fragment消失,另外的fragment还是可以照常工作。
  • 每个 fragment 的生命周期都是独立的,互不影响。

ViewModel的生命周期

ViewModel 的生命会绑定到调用ViewModelProvider 时传入的Lifecycle对象。直到Lifecycle对象永久的销毁了(Activity.finish()/fragment.detach()), ViewModel 都一直keep在内存中。

ViewModel vs SavedInstanceState

ViewModels 会在 configuration changes 时维持数据,但在系统kill应用时不会将数据持久化到本地。

举例说明,如果用户离开应用,几个小时后再回来,应用进程这时已经被系统kill,之前打开的Activity会根据save state重建现场。
所有的系统组件都会在SaveInstanceState 中保存状态, 你也可以在onSaveInstanceState中增加自己的数据。

onSaveInstanceState中存储的数据保存在系统进程的内存空间中,因此能存储的数据大小有限。

1.binder transaction: 1MB

The Binder transaction buffer has a limited fixed size, currently 1Mb, which is shared by all transactions in progress for the process. Consequently this exception can be thrown when there are many transactions in progress even when most of the individual transactions are of moderate size.

2.Saved Instance State

no doc etc.

尽量不要用这个存储非UI相关的数据。比如,用户通过UI查询一个国家的数据,这时候,放到saved instance state中的应该是countryId而不是Country