Jetpack系列-ViewModel使用和源码分析
1 简介和简单使用
1.1 简介
ViewModel
以注重生命周期的方式存储和管理界面相关的数据。ViewModel
类让数据可在发生屏幕旋转等配置更改后继续留存,保证数据的安全持久性。
如果Activity/Fragment
销毁或重新创建界面,则存储在其中的任何瞬态界面相关数据都会丢失。对于简单的数据,Activity
可以使用onSaveInstanceState()
方法从onCreate()
中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据。
另一个问题是,Activity/Fragment
经常需要进行可能需要一些时间才能返回的异步调用。Activity/Fragment
需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。
而ViewModel
在Actiivity
重建时会自动保存,可以确保数据不会丢失,并且ViewModel
的生命周期比较长,ViewModel
对象存在的时间范围是获取ViewModel
时传递给ViewModelProvider
的Lifecycle
。ViewModel
将一直留在内存中,直到限定其存在时间范围的Lifecycle
永久消失:对于Activity
,是在Activity
完成时;而对于Fragment
,是在Fragment
分离时。
ViewModel
的生命周期:
Google官方文档:developer.android.google.cn/topic/libra…
1.2 使用
创建RandomVideModel
来保存数据,继承RandomVideModel
。
class RandomVideModel : ViewModel() {
var num: Int = 0
}
在Activity
中实例化RandomVideModel
,并从中获取数据。
class VideModelActivity : AppCompatActivity() {
private lateinit var randomVideModel: RandomVideModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_vide_model)
randomVideModel = ViewModelProvider(
this,
ViewModelProvider.NewInstanceFactory()
).get(RandomVideModel::class.java)
tv_content.text = "${randomVideModel.num}"
btn_random.setOnClickListener {
tv_content.text = "${++randomVideModel.num}"
}
}
}
2 源码分析
首先看下ViewModel
的源码,ViewModel
代码很简单,内部用一个HashMap
存储数据。
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<>();
...
}
ViewModel
有3个子类,AndroidViewModel
、FragmentManagerViewModel
、LoaderViewModel
。
AndroidViewModel
内部持有一个Application
,可以方便使用上下文。
可以看到,是业务层Activity
中ViewModel
不是直接new
出来的,而是通过实例化一个ViewModelProvider
对象,然后调用ViewModelProvider
的get方法,传入了ViewModel
的class文件,所以,ViewModel
是通过反射创建出来的。
ViewModelProvider
的构造方法,传入两个参数,一个是ViewModelStoreOwner
,一个是Factory
。AppCompatActivity
继承的是ComponentActivity
,ComponentActivity
实现了ViewModelStoreOwner
接口,所以这里传this
就可以。另一个参数传Factory
的实现类NewInstanceFactory
。
public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
owner.viewModelStore,
factory
)
接着通过this调用了构造方法ViewModelProvider(ViewModelStore,Factory)
,ViewModelStore
就是存储ViewModel
的类,里边是一个HashMap
,提供了put
、get
、clear
方法。
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());
}
//遍历HashMap,清除所有数据
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
回到业务层Activity
中,调用ViewModelProvider
的get
方法传入了自定义ViewModel
的class。
@MainThread
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName
?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}
接着调用get(key: String, modelClass: Class<T>)
。在get
方法中,创建了ViewModel
对象,然后将ViewModel
存放到ViewModelStore
中。
@Suppress("UNCHECKED_CAST")
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
var viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody")
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = if (factory is KeyedFactory) {
factory.create(key, modelClass)
} else {
//由于传进来的是NewInstanceFactory,会走到这里。
factory.create(modelClass)
}
//将ViewModel存放到ViewModelStore中。
store.put(key, viewModel)
return viewModel
}
由于传入的是NewInstanceFactory
,所以最终调用的是Factory
接口中的create(modelClass: Class<T>)
。
public interface Factory {
public fun <T : ViewModel> create(modelClass: Class<T>): T
}
而具体实现在NewInstanceFactory
中,NewInstanceFactory
中的create
方法中最终通过反射创建了ViewModel
对象。
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
//通过反射创建ViewModel对象。
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);
}
}
来到ComponentActivity
中,上面说过,ComponentActivity
实现了ViewModelStoreOwner
接口。
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
ViewModelStoreOwner
中有一个接口方法getViewModelStore
,ComponentActivity
中实现了该方法。
@Override
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.");
}
ensureViewModelStore();
return mViewModelStore;
}
调用了ensureViewModelStore
去拿到ViewModelStore
。
void ensureViewModelStore() {
if (mViewModelStore == null) {
//这里的NonConfigurationInstances是ComponentActivity中的静态内部类。
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
ensureViewModelStore
方法中实例化了一个NonConfigurationInstances
,调用了getLastNonConfigurationInstance
,从NonConfigurationInstances
中拿ViewModelStore
。
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
而这个方法中又调用了mLastNonConfigurationInstances.activity
,点进去跳转到了Activity
类中的静态内部类NonConfigurationInstances
。
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
这里的activity
在retainNonConfigurationInstances
方法中被赋值。
NonConfigurationInstances retainNonConfigurationInstances() {
//调用onRetainNonConfigurationInstance获取activity
Object activity = onRetainNonConfigurationInstance();
...
NonConfigurationInstances nci = new NonConfigurationInstances();
//赋值给NonConfigurationInstances中的activity
nci.activity = activity;
...
return nci;
}
接着来到了Activity
类中的onRetainNonConfigurationInstance
,该方法的具体实现在ComponentActivity
类中。
Activity
中的onRetainNonConfigurationInstance
:
public Object onRetainNonConfigurationInstance() {
return null;
}
ComponentActivity
中的onRetainNonConfigurationInstance
:
public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
到这里,业务层Activity
就拿到了ViewModelStore
,就可以操作ViewModel
中的数据了。
而如何保证数据的稳健性不会丢失,可以看ComponentActivity
的构造方法。在ComponentActivity
无参构造方法中有如下代码:
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
...
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
mContextAwareHelper.clearAvailableContext();
// And clear the ViewModelStore
//如果配置了
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}
这里对Lifecycle
状态进行了监听,当状态处于ON_DESTROY
,也就是页面销毁时,先判断isChangingConfigurations
是否为true,为true的话,就不用调用getViewModelStore().clear()
,这样就保证了ViewModel
中数据的持久稳健性。
public boolean isChangingConfigurations() {
return mChangingConfigurations;
}
mChangingConfigurations
的值,默认是false,但是在因为Configuration
的改变被销毁了又重建的时候(最常见的就是横竖屏切换),mChangingConfigurations
的值为true,ViewModelStore
不调用clear
,数据不会清除。这就保证了Activity
销毁重建的时候数据不会丢失,还可以继续正常使用。
/** true if the activity is being destroyed in order to recreate it with a new configuration */
/*package*/ boolean mChangingConfigurations = false;