一、概述
在第二章的内容中,我们使用ViewModel
作为存储LiveData
的容器,其实它的功能要比我们看到的更为强大,其应用场景有如下几点。
1.1 应用场景
1.1.1 恢复 re-create 前的数据
在处理屏幕旋转所导致的Activity
重启时,我们期望可以保证数据不丢失。在之前,如果我们希望能够恢复这种re-create
行为所丢失的数据,那么就需要继承onSaveInstanceState()
方法,将数据进行序列化存储在bundle
中,之后在onCreate(Bundle savedInstanceState)
方法中从bundle
中取出数据。
1.1.2 维护异步回调
在日常的业务中,往往需要发起许多异步的操作,并等待其返回的结果。这时候我们就需要去维护这些逻辑,避免在destroy
后,回调还一直持有Activity
的实例,导致其不能回收,发生内存泄露。
1.1.3 简单的 UI 组件
可以看到,在UI
组件中包含了过多的逻辑,但其实UI
组件的职责应当是很简单的:展示数据 和 响应用户的行为,其它的逻辑都应当放在UI
组件之外。
1.2 目录框架
我们将从下面几个方面对ViewModel
做一个初步的了解:
- 使用
ViewModel
存储数据,并处理屏幕旋转 - 了解
ViewModel
的生命周期 - 通过
ViewModel
,处理Fragment
之间的交互 - 使用
ViewModel
替换loader
二、使用 ViewModel 存储数据,并处理屏幕旋转
在 Architecture Components 知识梳理(2) - LiveData 示例 中我们演示了如何在ViewModel
中存储数据,请大家先看一下前一篇文章中的内容:
/**
* LiveData 学习 Demo。
*/
public class LiveDataActivity extends AppCompatActivity {
private Button mBtnRefresh;
private TextView mTvResult;
private DataViewModel mViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_data);
mTvResult = findViewById(R.id.tv_result);
//1.创建 ViewModel。
mViewModel = ViewModelProviders.of(this).get(DataViewModel.class);
Log.d("DataViewModel", "createViewModel, model_address=" + mViewModel);
//2.添加观察者。
mViewModel.getWatcher().observeForever(new Observer<List<String>>() {
@Override
public void onChanged(@Nullable List<String> strings) {
Log.d("DataViewModel", "onChanged");
String tvDisplay = "";
for (String result : strings) {
tvDisplay += (result + "\n");
}
//4.数据发生了改变后会回调到这里。
mTvResult.setText(tvDisplay);
}
});
mBtnRefresh = findViewById(R.id.btn_refresh);
mBtnRefresh.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//3.触发加载。
Log.d("DataViewModel", "mViewModel.load()");
mViewModel.load();
}
});
}
@Override
protected void onResume() {
super.onResume();
Log.d("DataViewModel", "onResume()");
}
@Override
protected void onPause() {
super.onPause();
Log.d("DataViewModel", "onPause()");
}
}
普通的场景在之前已经说明过了,下面我们来看一下在 旋转屏幕 之后会发生什么。首先,我们进入界面,触发加载数据的操作,log
如下所示:
接着,我们旋转屏幕,这将会触发Activity
的重启,红色部分是触发重启后的操作:
这里我们可以发现两个现象:
- 新的
Activity
重新走了onCreate
方法,但是通过ViewModelProvider.of
方法获得的ViewModel
和之前仍然是同一个实例。 - 重启之后,触发了
observer
的onChanged()
方法,界面刷新后,又获得了上次保存的数据。
可以看到,并不需要做什么序列化的操作,也不需要进行手动的赋值操作,ViewModel
已经为我们处理好了。
三、ViewModel 的生命周期
ViewModel
的生命周期是和我们通过ViewModelProvider.of
操作符传入的UI
组件(Activity/Fragment)
绑定的,ViewModel
的实例将一直存在内存当中,直到UI
组件被销毁的时候ViewModel
也随之销毁。
对于Activity
来说是其finishes
的时候,而对于Fragment
,则是其detached
的时候。
四、在 Fragment 之间共享数据
当我们希望在Fragment
之间进行交互或者共享数据的时候,通常都是通过在它们共同的宿主Activity
声明接口,再由Activity
找到目标Fragment
去通知它。
当使用ViewModel
后,这一交互过程将会很简单。只需要让两个Fragment
都共享同一个ViewModel
(也就是说,ViewModelProvider.of(x)
传入的是它们共同的宿主Activity
),当一方的数据改变后,改变ViewModel
中的LiveData
,其它观察者就会收到通知,再去进行界面的刷新就可以了。
五、使用 ViewModel 替换 Loader
很久以前我们学习了Loader
的实现原理,Loader 知识梳理(1) - LoaderManager初探,它同样可以在屏幕旋转的时候进行自动的数据恢复,并通过CursorLoader
在数据发生变化的时候自动去重新加载数据。
现在我们已经可以使用
ViewModel+LiveData+Room
的方式来替换Loader
框架了:
-
ViewModel
:负责在UI
重建的时候保持数据不丢失。 -
Room
:在数据库内容发生变化的时候通知LiveData
。 -
LiveData
:在LiveData
的内容发生变化的时候通知UI
刷新。