视图与数据模型之间的桥梁ViewModel
在页面功能较为简单的情况下,我们通常会将UI交互、数据获取等相关的业务逻辑全部写在页面中。但是在页面功能复杂的情况下,这样做就是不合适的。因为这样不符合“单一功能原则”。页面只应该负责处理用户与UI控件的交互,应该将UI与数据相关的业务逻辑隔离开
为了能够更好地将职能划分,Android为我们提供了ViewModel类,专门用于存放应用程序页面所需的数据
image.png
ViewModel的生命周期特性
由于Android在横竖屏切换,存在数据恢复的问题。现在ViewModel能为我们解决这个问题。ViewModel独立于配置变化。也就是说屏幕旋转所导致的Activity重建,并不会影响ViewModel的生命周期
ViewModel的生命周期
由图可以看到,ViewModel的生命周期是贯穿在Activity生命周期的始终的
ViewModel的基本使用方法
- build.gradle 添加依赖
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
- 写一个继承自ViewModel的类,TimerViewModel
public class TimeViewModel extends ViewModel {
@Override
protected void onCleared() {
super.onCleared();
}
}
ViewModel是一个抽象类,其中只有一个onCleared()方法。当ViewModel不再被需要,即与之相关的Activity都被销毁时,该方法会被系统调用
- 我们在TimeViewModel中创建一个计时器Timer,每隔1s,通过OnTimerChangeListener通知它的调用者
public class TimeViewModel extends ViewModel {
private Timer timer;
private int currendSecond;
private OnTimeChangedListener timeChangedListener;
public void setTimeChangedListener(OnTimeChangedListener timeChangedListener) {
this.timeChangedListener = timeChangedListener;
}
public void startTiming(){
if (timer == null) {
timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
currendSecond++;
if (timeChangedListener != null) {
timeChangedListener.onTimeChanged(currendSecond);
}
}
};
timer.schedule(timerTask,1000,1000);
}
}
@Override
protected void onCleared() {
super.onCleared();
//release
timer.cancel();
}
interface OnTimeChangedListener{
void onTimeChanged(int second);
}
}
- 在Activity中监听OnTimeChangeListener,并更新UI
ViewModel的实例化过程,是通过ViewModelProvider来完成的,ViewModelProvider会判断ViewModel是否存在,弱存在则直接返回,若不存在则创建一个viewModel
public class TimerActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
initComponent();
}
private void initComponent() {
TextView timeTv = (TextView) findViewById(R.id.tv_second);
//创建viewModel
TimeViewModel timeViewModel = new ViewModelProvider(this).get(TimeViewModel.class);
timeViewModel.setTimeChangedListener(new TimeViewModel.OnTimeChangedListener() {
@Override
public void onTimeChanged(int second) {
runOnUiThread(new Runnable() {
@Override
public void run() {
timeTv.setText("TIME:" + second);
}
});
}
});
timeViewModel.startTiming();
}
}
运行并旋转屏幕,我们可以发现当旋转屏幕导致Activity重建时,计时器并没有停止。这意味着横竖屏状态下的viewModel是同一个,并没有被销毁
ViewModel的原理
我们通过new ViewModelProvider(ViewModelStoreOwner owner).get(Class clz)
来创建ViewModel对象,而这里我们传入的this,指代当前的Activity。这是因为我们的Activity继承自ComponentActivity,而ComponentActivity默认实现了ViewModelStoreOwner接口
接口方法getViewModelStore()返回创建的ViewModelStore()
image.png
从ViewModelStore的源码可以看出,ViewModel实际上是以HashMap<String,ViewModel>的形式被缓存起来了,ViewModel与页面之间没有直接的关联。通过ViewModelProvider进行关联。当页面需要ViewModel时,会向ViewModelProvider索要,ViewModelProvider会检查该ViewModel是否已经存在于缓存中,若存在则直接返回,若不存在,则实例化一个。因此,Activity由于配置变化导致的销毁重建并不会影响ViewModel,ViewModel是独立于页面存在的
ViewModel与AndroidViewModel
由于ViewModel是独立页面的生命周期存在的,所以在使用ViewModel时不要将任何类型的Context或者含有Context引用的对象传入ViewModel。这样会导致内存泄漏,但是如果希望在ViewModel中使用Context,可以使用AndroidViewModel 它继承自ViewModel,并接收Application作为Context,这意味着,它的生命周期和Application是一样的。这就不会造成内存泄漏了
ViewModel与onSaveInstanceState()方法
onSaveInstanceState()方法只能保存少量的、并且必须是支持序列化的数据,而ViewModel则没有这个限制。ViewModel能支持页面中所有的数据。
网友评论