Android ViewModel 原理解析
带着疑问来看ViewModel的工作机制
什么是ViewModel
ViewModel是Android Jetpack架构组件中的一部分,它的设计目的是以注重生命周期的方式存储和管理界面相关的数据
在ViewModel出现之前,我们通常将数据直接存储在Activity/Fragment当中
直接将数据存储到Activity或者Fragment当中会出现什么问题了?
1.当配置变更,比如屏幕旋转,或者是应用切换语言/应用内切换黑白模式
答案:这种操作,当前的Activity的会销毁重建,存储在Activity当中的所有的临时数据都会随之丢失。那如果我们想要恢复数据,就需要使用onSaveInstanceState()
来保存或者恢复数据,但是这个方法只适合存储少量的可序列化的数据,不适合存储大量数据。
2.关于内存泄漏
如果一个Activity中启动了一个异步任务,比如说网络请求,并且在任务完成之前Activity被销毁了,那由于异步任务当中持有对Activity的引用,垃圾回收无法销毁这个Activity,从而导致内存泄漏。
3.职责过重
Activity/Fragment本事界面控制器,应该只负责处理UI和用户交互
如果同时处理网络数据数据库加载,处理业务逻辑,会导致类变得臃肿,难以测试和维护
正是由于上面这些问题,google工程师开发团队引入了ViewModel的概念
ViewModel本身对象存在的时间周期要比Activity/Fragment更长,它在配置更改的时候会自动保留,并在新的Activity或者Fragment创建的时候重新关联。
为什么设计出AndroidViewModel
我们先从代码的角度来分析这个问题,从下面看出,AndroidViewModel是继承与ViewModel,并且将Application作为构造函数的一个参数传递到ViewModel当中
从代码片段看出,这个AndroidViewModel有了使用应用上下文的能力
open class AndroidViewModel(private val application: Application) : ViewModel() {
/**
* Return the application.
*/
@Suppress("UNCHECKED_CAST")
open fun <T : Application> getApplication(): T {
return application as T
}
}
接下来我们分析一下为什么Google团队要专门设计一个AndroidViewModel出来
在标准的ViewModel中,我们是强烈建议不要持有对Activity,View的引用,这个我们在文章的上面有介绍,因为会导致内存泄漏的发生。
但是了,有的时候就是需要这个上下文去访问应用资源,或者系统服务
那google就给出了标准答案,这样就可以使用AndroidViewModel.
有没有很好奇,我们创建一个自定义的AndroidViewModel
这个AndroidViewModel中的Application是怎么传进去的?
看到这通过文字可能无法领会其意思,直接来看代码
class MyViewModel(application: Application) : AndroidViewModel(application) {
// 访问字符串资源
fun getStringResource(): String {
return getApplication<Application>().getString(R.string.app_name)
}
}
比如上面的,我们自定义一个MyViewModel,属于AndroidViewModel的viewmodel
再来看看怎么进行实例化的操作
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyViewModel myViewModel = new ViewModelProvider(this).get(MyViewModel.class);
}
}
MyViewModel myViewModel = new ViewModelProvider(this).get(MyViewModel.class);这个方法并没有去传application
那怎么就能在model里面使用了?
来分析ViewModelProvider(this).这个在干什么?
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory else instance
从上面的源码可以看出会有个默认的工厂去创建相关实例:defaultFactory(owner),
HasDefaultViewModelProviderFactory这是个代理工厂接口,会发现activity,fragment会实现这个接口 我们来看具体的代理工厂是什么?
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
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 (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
SavedStateViewModelFactory 这个就是具体的代理工厂
在结合ViewModelProvider(this).get(MyViewModel.class)这个get方法
就会走到代理工厂的create的方法 ,也就是SavedStateViewModelFactory 的create方法,下面就是具体的源码
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
val key = extras[ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY]
?: throw IllegalStateException(
"VIEW_MODEL_KEY must always be provided by ViewModelProvider"
)
return if (extras[SAVED_STATE_REGISTRY_OWNER_KEY] != null &&
extras[VIEW_MODEL_STORE_OWNER_KEY] != null) {
val application = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]
val isAndroidViewModel = AndroidViewModel::class.java.isAssignableFrom(modelClass)
val constructor: Constructor<T>? = if (isAndroidViewModel && application != null) {
findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE)
} else {
findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE)
}
// doesn't need SavedStateHandle
if (constructor == null) {
return factory.create(modelClass, extras)
}
val viewModel = if (isAndroidViewModel && application != null) {
newInstance(modelClass, constructor, application, extras.createSavedStateHandle())
} else {
newInstance(modelClass, constructor, extras.createSavedStateHandle())
}
viewModel
} else {
val viewModel = if (lifecycle != null) {
create(key, modelClass)
} else {
throw IllegalStateException("SAVED_STATE_REGISTRY_OWNER_KEY and" +
"VIEW_MODEL_STORE_OWNER_KEY must be provided in the creation extras to" +
"successfully create a ViewModel.")
}
viewModel
}
}
通过上面的代码,挑重点来看,这个代理工厂最终会走到ViewModelProvider里面提供的ViewModelProvider.AndroidViewModelFactory,最终会走到AndroidViewModelFactory的create方法当中
ViewModelProvider.AndroidViewModelFactory
@Suppress("DocumentExceptions")
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
return if (application != null) {
create(modelClass)
} else {
val application = extras[APPLICATION_KEY]
if (application != null) {
create(modelClass, application)
} else {
// For AndroidViewModels, CreationExtras must have an application set
if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
throw IllegalArgumentException(
"CreationExtras must have an application by `APPLICATION_KEY`"
)
}
super.create(modelClass)
}
}
}
val application = extras[APPLICATION_KEY]
if (application != null) {
create(modelClass, application)
}
从这就可以看出来这个application就传到这里来了,我们就可以在这里用application了,实际上这个application其实就是activity/fragment里面直接获取的,通过viewmodel这中工厂设计模式巧妙的传到我们自定义的AndroidViewModel当中
class MyViewModel(application: Application) : AndroidViewModel(application) {
// 访问字符串资源
fun getStringResource(): String {
return getApplication().getString(R.string.app_name)
}
}
从一个小小的AndroidViewModel中的application的传值就可以让我们了解到ViewModelProvider创建viewmodel的神奇之处,这里面的这种工厂设计理论可以让我借鉴到实际业务开发当中
为什么屏幕旋转或者切换语言ViewModel的实例会是同一个
要回答这个问题,我们先来了解一下ViewModelStore这个类
open class ViewModelStore {
private val map = mutableMapOf<String, ViewModel>()
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun put(key: String, viewModel: ViewModel) {
val oldViewModel = map.put(key, viewModel)
oldViewModel?.onCleared()
}
/**
* Returns the `ViewModel` mapped to the given `key` or null if none exists.
*/
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
operator fun get(key: String): ViewModel? {
return map[key]
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun keys(): Set<String> {
return HashSet(map.keys)
}
/**
* Clears internal storage and notifies `ViewModel`s that they are no longer used.
*/
fun clear() {
for (vm in map.values) {
vm.clear()
}
map.clear()
}
}
上面这个类在干什么?
这个 ViewModelStore 类是 Android Jetpack Lifecycle 库中的核心组件,它的主要作用是:
存储和管理ViewModel实例的生命周期
ViewModel的存储 使用 MutableMap<String, ViewModel> 来存储 ViewModel 实例
每个 ViewModel 通过唯一的 key 进行标识和检索,
这个key类似与 // key 类似: “androidx.lifecycle.ViewModelProvider.DefaultKey:com.example.MyActivity”
所以具备了当配置发生变化(如屏幕旋转)时,保持 ViewModel 实例不被销毁
当组件真正被销毁时,调用 clear() 方法清理所有 ViewModel
屏幕旋转或者切语言理论是不是正真意义上的销毁,原生代码会通过标记来说这一点 这里不具体分析
所以当屏幕旋转或者是切换语言,ViewModel的实例是同一个。
为什么使用ViewModel会发生数据倒灌
因为ViewModel 的存活机制,配置更新时候比如旋转屏幕或者切换语言viewmodel的实例时同一个 这个会发生数据倒灌的前提
下面来看一下LiveData的源码
// LiveData.java
public abstract class LiveData<T> {
private int mVersion = START_VERSION; // 版本号,初始为-1
// 设置数据时版本号+1
@MainThread
protected void setValue(T value) {
mVersion++; // 关键:每次设置数据版本号增加
mData = value;
dispatchingValue(null);
}
// 观察者包装类
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive = false;
int mLastVersion = START_VERSION; // 观察者最后接收的版本
void activeStateChanged(boolean newActive) {
if (newActive) {
considerNotify(); // 激活时检查是否需要通知
}
}
void considerNotify() {
// 关键判断:如果观察者的版本落后于LiveData的当前版本
if (mLastVersion >= mVersion) {
return; // 版本一致或更新,不通知
}
mLastVersion = mVersion;
mObserver.onChanged((T) mData); // 数据倒灌发生在这里!
}
}
}
从上面看出当配置发生变化 mLastVersion变成初始值-1,mVersion的值变成0最终走到
mObserver.onChanged((T) mData); // 数据倒灌发生在这里!
怎么解决这个数据倒灌的问题了?
小编这里推荐使用SingleLiveEvent
public class SingleLiveEvent<T> extends MutableLiveData<T> {
private static final String TAG = "SingleLiveEvent";
private final AtomicBoolean mPending = new AtomicBoolean(false);
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull final Observer<? super T> observer) {
if (hasActiveObservers()) {
KLog.w(TAG, "Multiple observers registered but only one will be notified of changes.");
}
// Observe the internal MutableLiveData
super.observe(owner, new Observer<T>() {
@Override
public void onChanged(@Nullable T t) {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t);
}
}
});
}
@MainThread
public void setValue(@Nullable T t) {
mPending.set(true);
super.setValue(t);
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
public void call() {
setValue(null);
}
}
具体的LiveData的分析会在下个章节详细的讲解。
更多推荐



所有评论(0)