概念
Android为我们提供了ViewModel类,专门用于存放应用程序页面所需的数据。也可以这样理解:它是介于View和Model之间的一个东西。它起到了桥梁的作用,使视图和数据既能分离开,也能够保持通信。这样更符合“单一职责原则”,页面负责展示数据以及处理用户交互。
官方注释:
ViewModels usually expose this information via {@link LiveData} or Android Data
Binding. You can also use any observability construct from you favorite framework.
ViewModel's only responsibility is to manage the data for the UI. It should never access your view hierarchy or hold a reference back to the Activity or the Fragment.
生命周期
ViewModel独立于配置变化,这意味着屏幕旋转所导致的Activity重建,并不会影响ViewModel的生命周期。概括一句话:将视图与数据分离,并独立于Activity的重建。
使用
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
ViewModel是一个抽象类,其中只有一个onCleared()方法。当ViewModel不再被需要,即与之相关的Activity被销毁时,该方法会被系统调用。我们可以在该方法中执行一些资源释放相关的操作。注意:由于屏幕旋转而导致的Activity重建,并不会调用此方法。
public abstract class ViewModel {
// Can't use ConcurrentHashMap, because it can lose values on old apis (see b/37042460)
@Nullable
private final Map<String, Object> mBagOfTags = new HashMap<>();
private volatile boolean mCleared = false;
/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
* <p>
* It is useful when ViewModel observes some data and you need to clear this subscription to
* prevent a leak of this ViewModel.
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
@MainThread
final void clear() {
......
onCleared();
}
......
}
示例:
public class MyViewModel extends ViewModel {
private Timer mTimerTask;
private int mCount;
public MyViewModel() {
Log.e("MyViewModel", "MyViewModel 创建了");
}
public void startTimer() {
if (mTimerTask == null) {
mTimerTask = new Timer();
}
mTimerTask.schedule(new TimerTask() {
@Override
public void run() {
Log.e("MyViewModel", "mCount -> " + mCount++);
}
}, 1000, 1000);
}
@Override
protected void onCleared() {
super.onCleared();
Log.e("MyViewModel", "onCleared");
if (mTimerTask != null) {
mTimerTask.cancel();
}
}
}
--------------------------------------
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("SecondActivity", "onCreate");
MyViewModel mMyViewModel = new ViewModelProvider(this).get(MyViewModel.class);
mMyViewModel.startTimer();
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.e("SecondActivity", "屏幕旋转了");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("SecondActivity", "onDestroy");
}
}
---------------------------------------
Log:
2020-08-09 11:24:48.856 10531-10531/com.json.jetpack E/SecondActivity: onCreate
2020-08-09 11:24:51.263 10531-10531/com.json.jetpack E/SecondActivity: 屏幕旋转了
2020-08-09 11:24:51.367 10531-10531/com.json.jetpack E/SecondActivity: onDestroy
2020-08-09 11:24:51.424 10531-10531/com.json.jetpack E/SecondActivity: onCreate
2020-08-09 11:24:53.990 10531-10531/com.json.jetpack E/SecondActivity: onDestroy
对应ViewModel的生命周期
2020-08-09 11:24:48.856 10531-10531/com.json.jetpack E/MyViewModel: MyViewModel 创建了
2020-08-09 11:24:49.859 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 0
2020-08-09 11:24:50.860 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 1
2020-08-09 11:24:51.860 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 2
2020-08-09 11:24:52.428 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 3
2020-08-09 11:24:52.861 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 4
2020-08-09 11:24:53.429 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 5
2020-08-09 11:24:53.861 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 6
2020-08-09 11:24:53.990 10531-10531/com.json.jetpack E/MyViewModel: onCleared
两点:
- ViewModel实例化的过程,是通过ViewModelProvider来完成的, ViewModelProvider会判断ViewModel是否存在,若存在则直接返回,否则,它会创建一个ViewModel
- 上面示例Log验证了ViewModel的生命周期,即配置的更改不影响ViewModel,只有用户主动销毁了Activity,ViewModel的onCleared方法会被调用,ViewModel的生命周期结束;
ViewModel原理
理解ViewModel原理的关键是ViewModelStore,从上面的示例我们看到,ViewModel的实例化过程是通过ViewModelProvider来实现的。
MyViewModel mMyViewModel = new ViewModelProvider(this).get(MyViewModel.class);
mMyViewModel.startTimer();
--------------------------------
private final ViewModelStore mViewModelStore;
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), ...);
}
// 上述ViewModelProvider的生成最终会调用下面的这个构造函数
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
结论:ViewModelProvider保存了当前页面对应的ViewModelStore, 并负责操作ViewModelStore。
---> 有点类似ThreadLocal与ThreadLocalMap的感觉
而生成ViewModelProvider对象的时候,我们传入了一个参数 - ViewModelStoreOwner,因为我们的Activity是CompatActivity的子类,而CompatActivity实现了ViewModelOwner接口:(PS: Fragment也默认实现了ViewModelOwner)
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
所以上面示例,我们传入了this。
可以看到实现ViewModelStoreOwner接口,只需实现一个方法,该方法返回ViewModelStore.
- ComponentActivity
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
可以看到,ViewModelStore是理解原理的关键:
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
ViewModeStore的源码很简单,简单的概括就是一个HashMap,缓存我们的ViewModel。
从ViewModelStore的源码可以看出,ViewModel实际上是以HashMap<String , ViewModel>的形式被缓存了起来。ViewModel与页面之间没有直接的关联,它们通过ViewModeProvider进行关联。
当页面需要ViewModel时,会向ViewModelProvider索要,ViewModelProvider检查该Model是否已经存在于缓存中了,若存在,则直接返回,若不存在,则实例化一个。
- ViewModelProvider
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
// Key的生成规则:【DEFAULT_KEY :canonicalName】
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
// 若存在,直接返回
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
// 若不存在,利用反射创建ViewModel
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
// 创建完,缓存到ViewModelStore中
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
因此,Activity由于配置变化导致的销毁重建并不会影响ViewModel,看下ComponentActivity的构造函数中的源码就立刻明白了:
- ComponentActivity
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {// 非配置更改的销毁
getViewModelStore().clear();
}
}
}
});
- ViewModelProvider
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
- ViewModel
@MainThread
final void clear() {
......
onCleared();
}
注意:不要向ViewModel中传入任何类型的Context或带有Context引用的对象,这可能导致页面无法被销毁,从而引发内存泄露。
ViewModel与AndroidViewModel
但是如果希望在ViewModel中使用Context怎么办?可以使用AndroidViewModel,它继承自ViewModel,并接收Application作为Context。这意味着,它的生命周期和Application是一样的。
ViewModel与onSaveInstanceState()方法
onSaveInstance:只能保存少量的,能支持序列化的数据,可以持久化页面的数据,即当页面被彻底销毁时,数据还存在。
ViewModel:能保存页面中所有的数据,但是ViewModel不支持数据的持久化,当界面被彻底销毁的时候,ViewModel及其持有的数据就不存在了。