首先,ViewModel是什么.
ViewModel是一个以注重生命周期存储和管理界面相关数据的类。ViewModel可以让数据在发生如屏幕旋转等配置更改后继续存留。
比如我现在在Activity里定义了一个Int型的变量x初始化为0。在Activity运行期间,我点击了一个按钮让x的值变为1。假如此时我旋转,等屏幕旋转后,Activity重建。那x的值会被重新设置为0。现在我们使用ViewModel,让x的值在屏幕旋转之后还是1。
开始使用ViewModel
实现一个ViewModel我们可以创建一个类来继承ViewModel类,并在里面定义一个Int型的变量,初始化为0.
class NumViewModel : ViewModel() {
var num: Int = 0
}
接着在Activity的onCreate()方法中获取我们的NumViewModel (在依赖了Android KTX 中的 Fragment KTX模块后,可以直接使用viewModels和activityViewModels属性委托绑定到ViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val model = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
.get(NumViewModel::class.java)
}
现在我点击一个按钮让NumViewModel中的x的值变为1。假如此时旋转屏幕,在Activity重建后,NumViewModel中x的值还是为1。
使用ViewModel在Fragment之间共享数据
我们可以使用ViewModel在Fragment之间共享数据,只要我们的Fragment获取ViewModel时是使用的是包含他们的Activity,这里使用的是requireActivity()方法,他返回一个不为空的Activity。此时他们获取的是同一个SharedViewmodel的实例
class SharedViewModel : ViewModel() {
val selected = MutableLiveData()
fun select(string: String) {
selected.value = string
}
}
class OneFragment : Fragment(){
private lateinit var sharedViewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedViewModel = ViewModelProvider(requireActivity(),
ViewModelProvider.NewInstanceFactory()).get(SharedViewModel::class.java)
}
}
class TwoFragment : Fragment(){
private lateinit var sharedViewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedViewModel = ViewModelProvider(requireActivity(),
ViewModelProvider.NewInstanceFactory()).get(SharedViewModel::class.java)
}
}
ViewModel对象的获取
我们在创建ViewModel对象的时候使用了ViewModelProvider,下面是它的构造方法:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
在上面的构造方法中,需要传递一个ViewModelStoreOwner和一个Factory,ViewModelStoreOwner是一个接口,提供了一个获取ViewModelStore的方法,而我们的FragmentActivity已经实现了这个接口,所以我们在上面的Activity中直接传递了一个this。
接下来我们看一下FragmentActivity获取ViewModelStore的过程。
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
public class FragmentActivity extends ComponentActivity implements
ViewModelStoreOwner,
ActivityCompat.OnRequestPermissionsResultCallback,
ActivityCompat.RequestPermissionsRequestCodeValidator {
...
@NonNull
@Override
public ViewModelStore getViewModelStore() {
...
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的过程中,会首先获取一个NonConfigurationInstances,在从该对象中获取ViewModelStore。如果NonConfigurationInstances中的ViewMdeolStore为空,才会去新建一个ViewModelStore。
下面是ViewModelStore。
public class ViewModelStore {
//使用一个HashMap来维护我们的ViewModel
//是因为一个Activity或Fragment中可能拥有多个ViewModel
private final HashMap mMap = new HashMap<>();
//将ViewModel对象添加进mMap中
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
//调用get方法获取ViewModel对象
final ViewModel get(String key) {
return mMap.get(key);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
接着我们看一下Factory,这是ViewModelProvider里面的一个接口。提供了创建一个ViewModel的接口。而我们使用了一个它的实现类NewInstanceFactory
public interface Factory {
@NonNull
T create(@NonNull Class modelClass);
}
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public T create(@NonNull Class modelClass) {
//noinspection TryWithIdenticalCatches
//可以看到,这里是通过反射创建的对象
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
接着我们看一个ViewModelProvider的get()方法
@NonNull
@MainThread
public T get(@NonNull Class modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@NonNull
@MainThread
public T get(@NonNull String key, @NonNull Class modelClass) {
//首先从ViewModelStore中获取ViewModel
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
到这里我们看完了获取ViewModel的整个流程。
首先,我们通过Activity或Fragment获取一个ViewModelStore对象,在该对象的内部维护了一个HashMap用来存储我们Activity或Fragment的ViewModel。使用HashMap的原因是因为一个Activity或Fragment可能拥有多个的ViewModel。
接着,通过ViewModelStore对象的来获取一个ViewModel对象,判断一下获取的ViewModel对象能不能强转成我们传递进来的ViewModel类型。如果能,直接返回该对象,如果不能则通过Factory创建一个ViewModel对象,而Factory是通过反射创建ViewModel对象的。
最后,将Factory创建的ViewModel对象存储进ViewModelStore中,接着返回该对象。下次调用就可以直接返回ViewModelStore中存储的ViewModel对象。
现在我们看到,如果创建ViewModelProvider对象时使用的是同一个ViewModelStoreOwner实例的话。得到的ViewModel将是同一个对象,这样我们就可以共享ViewModel里面的数据了
ViewModel是如何在Activity重建后还保存有原来的ViewModel的
在我们因为配置的变动,如旋转屏幕时重建Activity时,AMS会调用到ActivityThread里的handleRelaunchActivity()方法
#ActivityThread
@Override
public void handleRelaunchActivity(ActivityClientRecord tmp,
PendingTransactionActions pendingActions) {
...
r.activity.mChangingConfigurations = true;
...
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
...
}
在该方法内部会将Activity的成员变量mChangingConfigurations设置为true,这一步很重要。接着调用handleRelaunchActivityInner()
#ActivityThread
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
List pendingResults, List pendingIntents,
PendingTransactionActions pendingActions, boolean startsNotResumed,
Configuration overrideConfig, String reason) {
...
//准备关闭Activity
handleDestroyActivity(r.token, false, configChanges, true, reason);
...
//重新启动Activity
handleLaunchActivity(r, pendingActions, customIntent);
}
#ActivityThread
@Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
// getNonConfigInstance为true
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance, reason);
...
}
#ActivityThread
//该方法在Activity的onDestroy()前调用
ActivityClientRecord performDestroyActivity(...boolean getNonConfigInstance, ...) {
...
// getNonConfigInstance为true
if (getNonConfigInstance) {
try {
//保存数据
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
...
}
}
...
}
Activity的retainNonConfigurationInstances()方法又会调用onRetainNonConfigurationInstance()方法
//在原本的Activity因配置改变而关闭之前调用
#Activity
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
...
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
...
return nci;
}
//在原本的Activity因配置改变而关闭之前调用
#FragmentActivity
@Override
public final Object onRetainNonConfigurationInstance() {
...
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;
}
到这里,就可以看到。在配置更改导致的原有的Activity关闭前,会将原本Activity内的ViewModelStore对象最终绑定到ActivityThread中的ActivityClientRecoed对象的lastNonConfigurationInstances属性上。当Activity关闭回调onDestory()时,由于mChangingConfigurations为true,ViewModelStore也不会清除里面的ViewModel。
接着Activity会重建,在重建时,会在attach方法中将原来保存在ActivityClientRecoed对象上的lastNonConfigurationInstances赋值给Activity的mlastNonConfigurationInstances
#Activity
final void attach(..., lastNonConfigurationInstances, ...){
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
现在,重新获取ViewModelStore时,由于mLastNonConfigurationInstances不为空,会直接获取里面的ViewModelStore对象。而这个对象就是重建前Activity的。
@NonNull
@Override
public ViewModelStore getViewModelStore() {
...
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;
}
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
到这里,Activity重建后就会重新连接到原来的ViewModelStore了。而ViewModelStore里又保存有原来的ViewModel。所以,ViewModel还是原来的。那么自然数据也就不会变化了。