ViewModel是 Android Jetpack 所提供的架构组件,用于分离 UI 逻辑与 UI 数据。ViewModel是用来保存应用UI数据的类,他会在配置变更(即 Configuration Change)之后继续存在。ViewModel的生命周期会比创建它的Activity、Fragment的生命周期更长。
从图中可以看出,在第一次调用Activity对象的onCreate()方法时创建了一个ViewModel。在Activity运行过程中可能会多次调用onCreate()方法(比如当设备屏幕旋转时),但是ViewModel一直存在,直到Activity结束并销毁。这意味着ViewModel不会因为它的创建者的一个配置变化而被销毁,Activity 的新实例将与现有的ViewModel重新连接。
(1)Activity或Fragment进行销毁和重建的时候,ViewModel的数据不会被回收。对于一些简单的数据,Activity可以使用onSaveInstanceState()方法,并从onCreate的bundle中重新获取,但这一方法仅仅适合一些简单的UI状态,对于列表型这种庞大的数据类型并不适合。当然,这样的优势不仅可以有利于数据的保存,而且可以节约资源。Activity中通常会有有那种在其创建的时候获取数据,然后在其销毁的时候释放数据的方法。如果这些放在Activity中的话,在Activity进行重建的时候,会很浪费资源。但是如果方法在ViewModel中的话,Activity的重建将不会导致数据的重复获取。
(2)ViewModel可以有效的瓜分责任,便于解耦。Android开发时一个常见的坑,是将很多的变量、数据、逻辑一起放在Activity或Fragment中,这样的代码就比较混乱和难以维护。显然,这种开发模式违反了“单一职责”的原则。但如果我们使用ViewModel就可以解决这一问题。ViewModel可以保存Activity中所有的UI数据,而Activity仅负责如何在屏幕上显示该数据和接收用户互动,但是它不会处理这些互动。
如果应用需要加载和存储数据,可以创建一个Repository存储区类。此外为了保证ViewModel不会因为处理过多逻辑而变得臃肿,可以创建一个Presenter类,来处理UI数据。具体架构如下:
首先来看一下官方所推荐的技术开发架构,如图所示:
由图中可以看出,UI,也就是Activity或Fragment,它的职责仅仅是视图的管理(大部分是刷新工作),相当于是一个ViewController的角色。ViewModel类相当于数据集散地,UI要这个数据了,ViewModel就去帮它在仓库找好,无论是数据库还是网络都行。ViewModel拿到数据之后就通知UI,通常情况下这个通知由LiveData来完成。最后通过LiveData去找DataBinding,完成数据的刷新。
ViewModel的使用可以用作技术架构的层次结构(如:MVVM架构的ViewModel层)去设计,也可以单独使用。
单独使用ViewModel,可以独立出UI数据的存储和管理逻辑。如下图所示:
不使用ViewModel:
使用ViewModel:
单独使用ViewModel,主要是来发挥其生命周期长,数据不易回收的特性。使用方法如下:
(1)引入lifecycle扩展库(不引入的话,就无法使用ViewModelProviders来构建ViewModel对象)
dependencies {
...
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
}
(2)创建一个自定义的ViewModel,它继承自ViewModel,用来存储和管理UI数据
public class MyViewModel extends ViewModel {
public int number = 0;
}
(3)创建一个自定义ViewModel的实例,并通过它来进行数据的存取
public class MainActivity extends AppCompatActivity {
MyViewModel myViewModel;
TextView textView;
Button button1, button2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
textView = findViewById(R.id.textView);
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
textView.setText(String.valueOf(myViewModel.number));
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
myViewModel.number++;
textView.setText(String.valueOf(myViewModel.number));
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
myViewModel.number += 2;
textView.setText(String.valueOf(myViewModel.number));
}
});
}
}
该例子中创建ViewModel实例是通过ViewModelProviders这个类,并且从始至终都没有使用ViewModel的构造方法。需要注意的是,ViewModel默认构造函数是无参的,而通过ViewModelProviders的方法构建ViewModel实例时也只提供一个无参的实现。如果我们希望创建一个带参的ViewModel并实例化,应该通过ViewModelProvider.Factory来构建实例。
注意:最新版的 'androidx.lifecycle:lifecycle-extensions:2.2.0' 中,ViewModelProviders的API已过时,官方建议ViewModel实例的创建用ViewModelProvider来代替。
myViewModel = new ViewModelProvider(this).get(MyViewModel.class);
当然,这种情况并不多。ViewModel更多情况下是与其他的Jetpack组件一起使用。例如:
初次之外,ViewModel 还可以与 LiveData 、Data Binding 组合使用来进行响应式UI设计。即:当底层数据被更新时,UI也会相应的自动更新。
ViewModel + LiveData + Data Binding = 响应式UI
注意:
由于ViewModel比Activity或Fragment的生命周期都要长,当Activity/Fragment被销毁(例如旋转了屏幕),但是如果ViewModel仍持有Activity/Fragment的引用,就会造成内存泄漏。
此外,如果需要比ViewModel生命周期更长的Application类,则可以使用AndroidViewModel的子类。通过这个子类,可以直接使用Application的引用。
两者是相辅相成的。当进程被关闭时,ViewModel将会被销毁,但是OnSaveInstanceState将不受影响;ViewModel可以存储大量数据,OnSaveInstanceState智能存储有限个数据。
ViewModel和OnSaveInstanceState对比
ViewModel |
OnSaveInstanceState |
能度过配置变更 |
能度过配置变更和进程关闭 |
存储大量数据 |
存储少量数据 |
|
必须序列化 |