文章目录
- 前言
- 一、ViewModel 是什么?
- 二、使用步骤
- 1.新建View Model实例
- 2.原理分析
- 2.1 那么ViewModel 是怎么实现对数据的管理的呢?
- 2.2 分析ViewModel实例
- 3.ViewModel的使用
- 3.1 AndroidViewModel
- 总结
前言
大家如果想了解ViewModel的理论可以先去官方去了解下这里不做概述,直接干货
一、ViewModel 是什么?
ViewModel是生命周期的方式存储和管理界面相关的数据类。ViewModel 让数据可在发生屏幕旋转等配置更改后继续留存。如今在MVVM架构中担任VM的重要角色
二、使用步骤
1.新建View Model实例
support库 :
android.arch.lifecycle包下
androidx 库:
androidx.lifecycle包下
实现都是一样的
class MyViewModels() : ViewModel() {
private val _liveData by lazy { //懒加载
MutableLiveData<String>()
}
fun getLiveData() = _liveData.apply {
_liveData.value = "Hello , ViewModel"
}
}
通过源码可以知道ViewModel是一个抽象类所以我们只有继承它就可以实现自定义的ViewModel代码如下:
public abstract class ViewModel {
…
}
一般View Model都是配合LiveData使用进行对Activity、Fragment的数据管理使在UI界面得到共享数据自动实现数据观察者模式
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProviders
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// val vm = ViewModelProviders.of(this,MyViewModel.MyViewModelFactory(this)).get(MyViewModel::class.java)
// vm.info()
val vm = ViewModelProviders.of(this,).get(MyViewModels::class.java) //获取实例
vm.getLiveData().observe(this){
//观察数据
}
}
}
这样就最简单的完成了ViewModel对Activity的数据管理了
ViewModelProviders是我引入的一个依赖 如下:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0'
2.原理分析
2.1 那么ViewModel 是怎么实现对数据的管理的呢?
先从Activity入手,来到androidx.activity.ComponentActivity可以看到实现了一个ViewModelStoreOwner接口
接口很简单代码如下:
package androidx.lifecycle;
import androidx.annotation.NonNull;
/**
* A scope that owns {@link ViewModelStore}.
* <p>
* A responsibility of an implementation of this interface is to retain owned ViewModelStore
* during the configuration changes and call {@link ViewModelStore#clear()}, when this scope is
* going to be destroyed.
*/
@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
来到接口的实现方法:
@NonNull
@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.");
}
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;
}
从代码得知通过getLastNonConfigurationInstance获取实列,通过新建对象获取实列,我们进一步来到getLastNonConfigurationInstance进行分析源码给的注释:
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
/**
* Called by the system, as part of destroying an
* activity due to a configuration change, when it is known that a new
* instance will immediately be created for the new configuration. You
* can return any object you like here, including the activity instance
* itself, which can later be retrieved by calling
* {@link #getLastNonConfigurationInstance()} in the new activity
* instance.
我们知道这个方法是系统调用的那么我们又回到ComponentActivity类这里面一个重新方法onRetainNonConfigurationInstance()代码如下:
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
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配置对象进行获取这样就实现了Activity的数据保存(屏幕翻转)
再来到Fragment进行源码查看来到androidx.fragment.app.Fragment这里也实现了ViewModelStoreOwner接口那么和上面一样来到实现的方法如下:
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
return mFragmentManager.getViewModelStore(this);
}
从中可以得知对象是从FragmentManagerImpl类中获取的代码如下:
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
return mNonConfig.getViewModelStore(f);
}
mNonConfig这个是FragmentManagerViewModel类的实列那么进一步溯源:
public void attachController(@NonNull FragmentHostCallback host,
@NonNull FragmentContainer container, @Nullable final Fragment parent) {
if (mHost != null) throw new IllegalStateException("Already attached");
mHost = host;
mContainer = container;
mParent = parent;
if (mParent != null) {
// Since the callback depends on us being the primary navigation fragment,
// update our callback now that we have a parent so that we have the correct
// state by default
updateOnBackPressedCallbackEnabled();
}
// Set up the OnBackPressedCallback
if (host instanceof OnBackPressedDispatcherOwner) {
OnBackPressedDispatcherOwner dispatcherOwner = ((OnBackPressedDispatcherOwner) host);
mOnBackPressedDispatcher = dispatcherOwner.getOnBackPressedDispatcher();
LifecycleOwner owner = parent != null ? parent : dispatcherOwner;
mOnBackPressedDispatcher.addCallback(owner, mOnBackPressedCallback);
}
// Get the FragmentManagerViewModel
if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}
从这里就可以得知mNonConfig 的实列获取
从中看出有三种方法进行实例 :
parent :这个是从父FragmentManager中得到NonConfig
host : 这个就是管理Fragment的Activity实例从Activity中得到NonConfig
最后一种就是直接新建实例
2.2 分析ViewModel实例
首先不能直接使用new 因为这样每次新建的话就违背了数据保存的原则
实例如下:
val vm = ViewModelProviders.of(this).get(MyViewModels::class.java) //获取实例
为什么是这样呢?那么我们进一步分析
进入到ViewModel类可以看到有一个Map域的声明、还有一个clear函数
可以看到clear函数就是对Map里面的值进行清空。嗯,若有所思
先放着我们回到获取View Model实例的地方发现是通过ViewModelProviders调用了一个of函数点进去
这个可以理解方法重写嘛,不管调用哪个最终都会回到两个参数的方法
第一个参数为FragmentActivity至于为什么是FragmentActivity,因为它继承的ComponentActivity实现了ViewModelStoreOwner接口上面也有讲到
第二个参数Factory ,这里的Factory是ViewModelProvider里面的Factory ,这个参数后面会讲到
继续分析
第一行代码:检查Activity如果在Application的oncreate前调用呢 就直接抛出异常
第二行代码:判断是否有传入Factory ,如果没有就使用默认的AndroidViewModelFactory
返回:通过使用工厂模式,ViewModel的创建交由工厂完成,通过获取View对象ViewModelStore传入中ViewModelProvider。
getViewModelStore函数就是我们上面分析的ViewModelStoreOwner接口里面的实现方法
那么我们点击去ViewModelStore这个类
看到Map域里面存储的是ViewModel因为一个Activity中可能有多个ViewModel,所以需要一个Map来维护关系表,key为ViewModel的名字,value为ViewModel对象 通过put方法进行存储,是不是感觉和ViewModel类的实现特别像
再回到之前的代码
val vm = ViewModelProviders.of(this,).get(MyViewModels::class.java) //获取实例
通过of函数返回了一个ViewModelProvider追后调用了get函数点进去分析如图
方法体首先对传入的参数进行规范判断如图
最后通过get重载函数以Default_Key作为前缀加上ViewModel 的完整类名为key,获取ViewModel 对象。
从ViewModelStore中的储存单元Map中获取ViewModel
先看ViewModelStore中是否存在,如果存了就直接返回
如果不存在,好像什么也没做如图:
这段代码就是判断我们传入的factort字段我们之前没有传入factort字段所以它为默认的AndroidViewModelFactory
然后这里将factort进行了一个比较那我们先来了解下AndroidViewModelFactory
这个类进行溯源可以知道实现了ViewModelProvider.Factory接口
getInstance()函数获取该类的实例
create()创建ViewModel
完成分析 回到get函数继续我们的分析
- 之后对mFactory 和KeyedFactory进行了一个比较
- 前面分析知道这两个类都分别直接或间接的实现了ViewModelProvider.Factory接口
- 如果是KeyedFactory类型则调用KeyedFactory类里面带有key的create()函数 这个key就是DEFAULT_KEY + “:” + canonicalName(以Default_Key作为前缀加上ViewModel 的完整类名为key)
- 这个key是用来在原有ViewModelStore的储存单元中找到进行覆盖一个新的ViewModel
- 也就是为什么在原有的Viewmodel中添加一个带参数的构造器然后获取实例时没有传入Factory参数会报错的原因,在下面我会给大家演示
如果不是KeyedFactory类型通过调用ViewModelProvider.Factory接口create()函数进行反射创建一个ViewModel
然后将所创建的ViewModel添加到ViewModelStore储存单元中
最后返回ViewModel完成对ViewModel的获取实例
3.ViewModel的使用
当我们需要在ViewModel类中通过构造器传递数值时,我们需要通过一个ViewModelProvider.Factory接口去实现它的create函数进行重载源码如下:
public class ViewModelProvider {
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}}
那么我们的代码就可以这样写:
class MyViewModel(private val context: Context) : ViewModel() {
fun info(){
println("上下文对象: $context")
}
class MyViewModelFactory(private val contexts:Context) : ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T = MyViewModel(contexts) as T
}
}
通过这段代码我们重载了Factory并进行了覆盖原有create函数
那么我们继续运行后发现报错:
报错不能实例化,原因:我们之前在ViewModel类中重载了ViewModelProvider.Factory中的create函数使之覆盖了原来的AndroidViewModelFactory源码如下:
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
所以这里我们必须要传入一个ViewModelProvider.Factory对象
val vm = ViewModelProviders.of(this,MyViewModel.MyViewModelFactory(this)).get(MyViewModel::class.java)
vm.info()
最后运行完成成功打印上下文地址
3.1 AndroidViewModel
那么如果我们要在ViewModel中使用上下文对象呢?
我上面写的实例是不对的,VIewModel不能持有View的上下文对象
因为ViewModel在Activity因配置变化导致重建时会被保留,从生命周期的角度来说,ViewModel的生命周期可能会长于Activity的生命周期。
这说明我们在使用ViewModel时一定要注意,不能让其引用Activity或View,否则可能导致内存泄漏。
源码为我们提供了一个子类AndroidViewModel 这个类就可以自带上下文对象Application
public class AndroidViewModel extends ViewModel {
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
public <T extends Application> T getApplication() {
return (T) mApplication;
}
}
总结
ViewModel的分析就到这里了,大家多看看开源项目就知道ViewModel怎么用了