作者:不说话的匹诺槽
转载地址:https://juejin.cn/post/7079775840757612558
前言
今天想跟大家聊的是Jetpack
组件之ViewModel
, 之前对ViewModel
也是一知半解,只是从博客上大概了解过它的原理,自己并没有深入了解过,所以打算自己由浅入深的领略一下,本人自己感觉不算是聪明的那类人,连我都能看得懂,相信各位肯定没问题,人人都能看得懂!
先来看看ViewModel
是什么?
ViewModel
类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel
类让数据可在发生屏幕旋转等配置更改后继续留存。架构组件为界面控制器提供了
ViewModel
辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留ViewModel
对象,以便它们存储的数据立即可供下一个 activity 或 fragment 实例使用。
这两个介绍摘自于官网文档,其中说明ViewModel的作用,主要是保存UI数据的。我们来看看它的生命周期:
可以看到ViewModel
在UI整个生命周期范围内都是同一个状态内的,当UI销毁的时候才会执行onCleard()
操作,清除数据。
使用
接下来,我们看下ViewModel
的简单使用
首先我们创建一个ViewModel
class MainViewModel : ViewModel() {
val liveData = MutableLiveData<String>("这是LiveData的数据")
}
然后在UI中进行获取到MainViewModel
,并进行数据订阅
class MainActivity3 : AppCompatActivity() {
private lateinit var viewModel: MainViewModel
private lateinit var binding:MainFragmentBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_fragment)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
viewModel.liveData.observe(this){
binding.message.text = it
}
}
}
先实例化ViewModelProvider
,通过ViewModelProvider::get
方法来获取MainViewModel
的实例对象,通过这种创建形式,不难看出,ViewModel
的创建是通过反射来创建的。
那么 ViewModle
是如何保存数据的呢?今天就由浅入深的带大家来领略一下 ViewModel
的奥妙之处,保证人人都能看得懂!
原理
首先我们从ViewModelProvider
看起,先了解一下它的面目。
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner))
在这里,这个构造方法 新建了一个默认的工厂,然后调用了自己默认的构造方法。其中这个工厂就是用来创建ViewModel
的。
接下来看看get方法做了点什么?
@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)
}
这里就是增加了一个默认的Key
值,然后调用另外一个get
方法,这个默认Key
是
internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
@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 {
........
}
viewModel = if (factory is KeyedFactory) {
factory.create(key, modelClass)
} else {
factory.create(modelClass)
}
store.put(key, viewModel)
return viewModel
}
在这个方法中,可以看到,根据key
值从store
中获取ViewModel
对象,如果 类型正确 ,当即返回当前对象, 如果不正确的话,通过工厂创建新的ViewModel
对象,存储到store
中并返回。
我们先看看工厂是如何创建VIewModel
的
public open class NewInstanceFactory : Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return try {
modelClass.newInstance()
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
}
}
这是前面 ViewModelProvider
创建的默认工厂,最后通过 modelClass.newInstance()
创建了 ViewModel
的实例化对象。
接下来我们看看看 store
是什么?
store
的类型是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());
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
可以看到它里面维护了一个HashMap
,根据Key
值 来对ViewModel
进行存储。同时提供了clear()
方法,将所有ViewModel
清除。
这个ViewModelStore
是在什么时候创建的呢?
在上文提到的创建ViewModelProvider
的时候,可以看到,ViewModelStoreOwner
是由Activity
创建ViewModelProvider
的时候 传入的,然后调用owner
中的 getViewModelStore()
方法,获取ViewModelStore
,并传到构造方法里面的。
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner))
这是因为 AppCompatActivity
的父类 ComponentActivity
实现了 ViewModelStoreOwner
接口。
public interface ViewModelStoreOwner {
ViewModelStore getViewModelStore();
}
ViewModelStoreOwner
接口就很简单,只提供了一个 getViewModelStore()
方法 来获取ViewModelStore
,我们来看看它的实现。
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;
}
这里面只是简单返回了 mViewModelStore
对象 ,重要的是ensureViewModelStore()
方法。
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
这里主要是获取 NonConfigurationInstances
对象,然后从中获取到 viewModelStore
,如果NonConfigurationInstances
为空的话,就新建一个 ViewModelStore
对象。
接下来我们主要看下 getLastNonConfigurationInstance();
方法。
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
我将这个方法的注释做了翻译:
检索之前由onRetainNonConfigurationInstance返回的非配置实例数据。可以从对新实例的初始{@link #onCreate}和{@link #onStart}调用中获得,允许你从先前的实例中提取任何有用的动态状态。
简单来说,这个方法就是用来获取 onRetainNonConfigurationInstance ()
方法中存储的内容。
public Object onRetainNonConfigurationInstance() {
return null;
}
由系统调用,作为由于配置更改而销毁活动的一部分,当已知将立即为新配置创建新实例时。您可以在此处返回您喜欢的任何对象,包括活动实例本身,稍后可以通过调用
getLastNonConfigurationInstance()
新的活动实例来检索这些对象。
这样对比来看不难看出:
onRetainNonConfigurationInstance()
是在Activity
销毁的时候 进行存储信息。
getLastNonConfigurationInstance()
的作用是获取 存储的信息的。
当屏幕发生旋转的时候 ,会先调用 onRetainNonConfigurationInstance
先将数据进行保存,然后再通过 getLastNonConfigurationInstance
将保存的数据获取到。
我们来看看ComponentActivity
对 onRetainNonConfigurationInstance
的实现:
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
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;
}
mViewModelStore
是在ensureViewModelStore
方法中获取的对象( 可能是通过 getLastNonConfigurationInstance()
获取,可能是重新创建 ) ,如果这个 mViewModelStore
是空的话,就会尝试从NonConfigurationInstances
中获取,如果仍然是空,直接返回null
,如果不是空的话, 重新创建nci
进行存储。
那这些数据什么时候才会清除呢?
在 ComponentActivity
的无参构造中,对生命周期做了一个监听,当页面进行销毁的时候,并且没有配置更改的时候,会执行
mViewModelStore
的 clear()
方法,进行数据的释放操作。
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();
}
}
}
});
..........
}
整个流程,大概就是这样
总结
分析了这么多,我们捋一下:
-
我们的
Activity
的父类ComponentActivity
实现了ViewModelStoreOwner
接口,通过ViewModelProvider
使用默认工厂 创建了viewModel
,并通过唯一Key
值 进行标识,存储到了ViewModelStore
中。等下次需要的时候即可通过唯一Key
值进行获取。 -
由于
ComponentActivity
实现了ViewModelStoreOwner
接口,实现了getViewModelStore
方法,当屏幕旋转的时候,会先调用
onRetainNonConfigurationInstance()
方法将 viewModelStore
保存起来,然后再调用 getLastNonConfigurationInstance
方法将数据恢复,如果为空的话,会重新创建 viewModelStore
,并存储在全局中,以便以下次发生变化的时候,能够通过onRetainNonConfigurationInstance
保存起来。
- 最后当页面销毁并且没有配置更改的时候,会将
viewModelStore
中的数据 进行清除操作。
网友评论