ViewModel
处于数据逻辑层,他的生命周期贯穿整个宿主,如act因屏幕旋转销毁重建时,其依然存活,只有act.finish后,才会自动销毁,因此可以用他来维持宿主的数据状态。现在比较流行的方式是把他当做唯一数据源来驱动UI展示:
view层:view (act / fragment)
数据逻辑层:viewModel
数据源:repository (db / network)
另外,还可以通过共享viewModel实现页面间通信,如两个fragment共享act的一个viewModel。
Jetpack笔记代码
本文源码基于SDK 29
引入依赖:
def lifecycle_version = "2.2.0"
//extensions包含Lifecycles、LiveData、ViewModel
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
创建ViewModel
,
class CommonViewModel extends ViewModel {
public MutableLiveData<String> text = new MutableLiveData<>();
}
在布局文件中使用,
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.holiday.jetpackstudy.viewmodel_livedata.CommonViewModel" />
<variable
name="commonViewModel"
type="CommonViewModel" />
</data>
<TextView
android:id="@+id/tv_text"
android:text="@{commonViewModel.text}"
android:textSize="@dimen/tv_text_size" />
</layout>
在act中使用,
class ViewModelActivity extends BaseActivity {
CommonViewModel mCommonViewModel;
void onCreate(Bundle savedInstanceState) {
//传this,基于act创建viewModel
mCommonViewModel = ViewModelProviders.of(this).get(CommonViewModel.class);
mBinding.setCommonViewModel(mCommonViewModel);
//观察数据变化
mCommonViewModel.text.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
//更新UI
mBinding.tvText.setText(s);
}
});
//旋转屏幕重建act后,viewModel还是同一个实例
QrLog.e(String.valueOf(mCommonViewModel.hashCode()));
}
//点击按钮改变数据
public void changeData(View view) {
mCommonViewModel.text.setValue(String.valueOf(System.currentTimeMillis()));
}
}
在onCreate方法打印ViewModel的hashCode,可见屏幕旋转导致act重建时,mCommonViewModel还是同一个实例,
以ViewModelProviders.of(this).get(CommonViewModel.class)
为入口,先看ViewModelProviders.of
,
//ViewModelProviders.java
ViewModelProvider of(FragmentActivity activity) {
return of(activity, null);
}
ViewModelProvider of(FragmentActivity activity, Factory factory) {
if (factory == null) {
//工厂类,用来反射创建viewModel
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
//ViewModelStore用来存储ViewModel
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
这里只需知道ViewModelProviders.of
得到了当前act的ViewModelProvider
,接着看get
方法,
//ViewModelProvider.java
<T extends ViewModel> T get(Class<T> modelClass) {
//获取类全名
String canonicalName = modelClass.getCanonicalName();
//加上前缀,得到唯一标识key
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
<T extends ViewModel> T get(String key, Class<T> modelClass) {
//获取viewModel
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//如果已经存在,则直接返回
return (T) viewModel;
} else {
//忽略
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
//调用前边提到的工厂类,反射创建viewModel
viewModel = (mFactory).create(modelClass);
}
//存储viewModel
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
看起来代码不是很多,那么viewModel是如何实现act重建后依然存活的呢?
首先viewModel存储在mViewModelStore,而这个store是创建ViewModelProvider时传进来的,即activity.getViewModelStore()
,
//ComponentActivity.java
ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
//act屏幕旋转重建时,在这里取出ViewModelStore
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
//act第一次创建时,在这里创建了ViewModelStore
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
那NonConfigurationInstances
又是如何存活的呢?
//Activity.java
Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
mLastNonConfigurationInstances在attach
的时候获取值,
//Activity.java
void attach() {
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
Activity.attach
在ActivityThread.performLaunchActivity
中被调用,
//ActivityThread.java
Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
}
可见来源于ActivityClientRecord.lastNonConfigurationInstances
,查找一下可知,r.lastNonConfigurationInstances
在performDestroyActivity
时赋值,
//ActivityThread.java
performDestroyActivity(){
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
}
这边有点绕,再看到activity,
//Activity.java
NonConfigurationInstances retainNonConfigurationInstances() {
//具体实现看下一个方法
Object activity = onRetainNonConfigurationInstance();
onConfigurationInstances nci = new NonConfigurationInstances();
//nci.activity存储的是ComponentActivity里边的NonConfigurationInstances
nci.activity = activity;
return nci;
}
//ComponentActivity.java
public final Object onRetainNonConfigurationInstance() {
NonConfigurationInstances nci = new NonConfigurationInstances();
//这里的NonConfigurationInstances存储了viewModelStore
nci.viewModelStore = viewModelStore;
return nci;
}
至此,简单的概括一下就是,
Activity销毁时,借助
ActivityClientRcord
来间接保存viewModelStore
;Activity重建时,从ActivityClientRcord
中间接取出viewModelStore
。
而所有ActivityClientRcord
又被存储在ActivityThread
的成员变量里能长期存活:
//ActivityThread.java
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
至于act退出时viewModel可以自动销毁,是因为ComponentActivity
里添加了一个观察者:
//ComponentActivity.java
ComponentActivity() {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(LifecycleOwner source,Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
//如果是屏幕旋转或语言切换引起的destroy,则无需回调
if (!isChangingConfigurations()) {
//内部会遍历所有ViewModel,回调onCleared
getViewModelStore().clear();
}
}
}
});
}
关于Lifecycles
,可以阅读我的早些的笔记。
onSaveInstanceState
简单,但是viewModel
只能在屏幕旋转和语言切换后的页面重建维持数据,当页面意外销毁时数据无法恢复,而这点onSaveInstanceState
可以做到,关于viewModel
如何实现这一点,可以看我的下一篇笔记。