一、为什么使用ViewModel
通常情况下,我们会将UI交互、逻辑处理等业务逻辑也写在Activity/Fragment中,随着业务的迭代会导致页面代码臃肿且难以维护,并且不符合“单一责任”原则。页面应该只负责UI交互内容,相关数据的获取与逻辑处理应该单独存放和处理。
ViewModel是Android设计专门用于存放应用程序页面所需的数据。它将页面所需的数据从页面中剥离出来,页面只需要处理用户交互,以及负责展示数据的工作
ViewModel还有一重大功能就是解决资源配置导致的销毁重建问题。如果资源配置发生变更,我们需要考虑数据的存储与恢复,若不进行存储,则需要重新获取数据。而ViewModel独立于资源配置变化,即使资源配置变化导致Activity页面重建,也不会影响ViewModel的生命周期。
简单来说,ViewModel旨在以注重生命周期的方式存储和管理界面相关的数据,让数据再发生屏幕旋转等配置更改后继续留存,对View和Model之间起到了桥梁通信的作用。
ViewModel 对象存在的时间范围是获取 ViewModel时传递给ViewModelProvider 的 Lifecycle。ViewModel将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 activity,是在 activity 完成时;而对于 fragment,是在 fragment 分离时
二、基本使用
依赖引入
dependencies {
def lifecycle_version = "xxx"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
// Saved state module for ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
// Annotation processor
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
}
2.1 简单使用
ViewModel类主要负责为界面准备数据,并在配置更改期间自动保留ViewModel对象。
例如:在ViewModel中获取和存储用户信息
定义一个User数据类
data class User(var age:Int,var name:String)
实现viewModel类
class UserModel:ViewModel() {
val mUserLiveData:MutableLiveData<User> = MutableLiveData()
init {
//模拟从网络加载用户数据
mUserLiveData.postValue(User(1,"name1"))
}
fun doSomething(){
val user = mUserLiveData.value
if (user != null){
user.age = 15
user.name = "name15"
mUserLiveData.value = user
}
}
}
在Activity中使用ViewModel,获取User信息
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
text = findViewById(R.id.tv_text)
val userModel: UserModel = ViewModelProvider(this)[UserModel::class.java]
userModel.mUserLiveData.observe(this){
text.text = it.toString()
}
text.setOnClickListener(){
userModel.doSomething()
}
}
}
点击按钮,user中age变为15,之后旋转屏幕,则TextView显示的age还是15
ViewModel绝不能引用视图、Lifecycle,或可能存储对 Activity 上下文的引用的任何类,否则可能会导致内存泄漏,因为ViewModel中有在Activity销毁之后才会自动销毁。
ViewModel对象存在的时间比视图或LifecycleOwner的特定实例存在的时间长。如果ViewModel需要Application上下文,可以扩展AndroidViewModel类并设置用于接收Application的构造函数。
2.2 Fragment间通信
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。Activity和Fragment可以通过共享一个ViewModel解决这一通信问题,因为Fragment是依附在Activity上。
实例化ViewModel时,将该Activity传入ViewModelProvider,会给你一个该Activity创建好的ViewModel,这个Fragment可以方便的访问该ViewModel中的数据,在Activity中修改userModel数据后,该Fragment就能拿到更新后的数据
class SharedViewModel:ViewModel() {
private val selected:MutableLiveData<String> = MutableLiveData()
fun select(string: String){
selected.value = string
}
fun getSelected():LiveData<String>{
return selected
}
}
class MasterFragment : Fragment() {
private var model: SharedViewModel? = null
fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//获取依附的Activity的ViewModel
model = getActivity()?.let { ViewModelProvider(it)[SharedViewModel::class.java] }
stringSelector.setOnClickListener { item -> model!!.select(item) }
}
}
class DetailFragment: Fragment(){
private var model:SharedViewModel? = null
fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
//获取依附的Activity的ViewModel
model = activity?.let { ViewModelProvider(it)[SharedViewModel::class.java] }
model?.getSelected()?.observe(this,{
//更新UI
})
}
}
前述MasterFragment和DetailFragment都可以拿到Activity的ViewModel,拿到了该ViewModel就可以拿到里面的数据,相当于间接通过ViewModel通信
通过ViewModel实现Fragment通信的优势:
Activity不需要执行任何操作,也不需要对此通信有任何了解。- 除了
SharedViewModel约定之外,Fragment不需要相互了解 - 每个
Fragment都有自己的生命周期,而不受另一个Fragment的生命周期的影响。
三、原理解析
关于ViewModel原理解析,基于2.4.0版本
先了解一下ViewModel的几个核心角色
-
ViewModelProviderViewModel的使用工具类,封装了一系列作用方法,用于创建ViewModel -
ViewModelStore存储多个
ViewModel的存储类 -
ViewModelStoreOwnerViewModel存储器的拥有者,Activity、Fragment都是其实现者 -
SavedStateHandleViewModel的功能扩展,使得开发者无需直接使用onSaveInstanceState(Bundle)等方法来完成数据的保存和重建,而只需要在ViewModel里来完成即可 -
......
ViewModel的原理机制,可以参看下图
ViewModel构造过程:
ViewModelStore树:
ViewModelStore销毁逻辑:
ViewModel的原理,简单总结为:
- 所有实例化的
ViewModel都缓存在ViewModelStore的封装对象中,实质是一个HashMap ViewModelStore与具体的Controller(Activity/Fragment)绑定,与其俱生俱灭,生命周期一样长- 获取
ViewModel的实例过程委托给ViewModelProvider工具类,其包含一个创建ViewModel的工厂类Factory和一个对ViewModelStore的引用 - 获取
ViewModel时,会先从ViewModelStore中获取缓存的ViewModel,若没有缓存过则用Facotry实例化一个新的ViewModel并缓存 viewModelStore存储在NonConfigurationInstances类的mLastNonConfigurationInstances中,其是存在ActivityClientRecord中的一个组件信息,ActivityClientRecord是存在ActivityThread的mActivities中,由于ActivityThread不受Activity重建影响,由此类推,ViewModel不受Activity重建影响Activity在收到ON_DESTROY事件,如果判断到是由于配置项更改导致了Activity被销毁,那么就不会调用getViewModelStore().clear(),ViewModel内数据得以保留
3.1 ViewModel存储与获取
从前述代码的ViewModelProvider(this)[UserModel::class.java]入手
//创建ViewModelProvider,并将其保留在给定ViewModelStoreOwner的存储区
public open class ViewModelProvider(
private val store: ViewModelStore,
private val factory: Factory
){
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner))
}
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory else instance
@JvmStatic
public val instance: NewInstanceFactory
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
get() {
if (sInstance == null) {
sInstance = NewInstanceFactory()
}
return sInstance!!
}
AppCompatActivity的父类ComponentActivity与Fragment类已经实现了ViewModelStoreOwner和HasDefaultViewModelProviderFactory接口,可以直接从中获取到ViewModelStore和Factory的实例。当然我们也可以传入自己自定义实现的Factory实例。
下面看一下ViewModelStore和Factory的内部实现
3.1.1 Factory
Factory是ViewModelProvider的一个内部接口,实现类用于构建ViewModel实例
public interface Factory {
/**
* Creates a new instance of the given `Class`.
*/
public fun <T : ViewModel> create(modelClass: Class<T>): T
}
对于Factory,主要关注其两个实现类,分别是NewInstanceFactory和AndroidViewModelFactory
NewInstanceFactory实现类
NewInstanceFactory是通过反射来初始化构造函数无参数类型的ViewModel
@Suppress("SingletonConstructor")
public open class NewInstanceFactory : Factory {
@Suppress("DocumentExceptions")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return try {
//反射实现,传入的ViewModelClass必须包含无参构造函数
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)
}
}
public companion object {
private var sInstance: NewInstanceFactory? = null
/**
* Retrieve a singleton instance of NewInstanceFactory.
*/
@JvmStatic
public val instance: NewInstanceFactory
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
get() {
if (sInstance == null) {
sInstance = NewInstanceFactory()
}
return sInstance!!
}
}
}
AndroidViewModelFactory实现类
AndroidViewModelFactory专门用来实例化ViewModel中带Context的对象。
public open class AndroidViewModelFactory(
private val application: Application
) : NewInstanceFactory() {
@Suppress("DocumentExceptions")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
try {
modelClass.getConstructor(Application::class.java).newInstance(application)
} catch (e: NoSuchMethodException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InvocationTargetException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
} else super.create(modelClass)
}
public companion object {
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory else instance
internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
private var sInstance: AndroidViewModelFactory? = null
/**
* Retrieve a singleton instance of AndroidViewModelFactory.
*/
@JvmStatic
public fun getInstance(application: Application): AndroidViewModelFactory {
if (sInstance == null) {
sInstance = AndroidViewModelFactory(application)
}
return sInstance!!
}
}
}
AndroidViewModelFactory通过构造方法给ViewModel带入Application,则可以在ViewModel中拿到Context,因为Application是APP全局的,那么不存在内存泄露的问题。
HasDefaultViewModelProviderFactory实现
ComponentActivity和Fragment都实现了HasDefaultViewModelProviderFactory接口,看一下其与Factory的联系。
以ComponentActivity为例
public interface HasDefaultViewModelProviderFactory {
@NonNull
ViewModelProvider.Factory getDefaultViewModelProviderFactory();
}
@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;
}
public SavedStateViewModelFactory(@Nullable Application application,
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs) {
mSavedStateRegistry = owner.getSavedStateRegistry();
mLifecycle = owner.getLifecycle();
mDefaultArgs = defaultArgs;
mApplication = application;
mFactory = application != null
? ViewModelProvider.AndroidViewModelFactory.getInstance(application)
: ViewModelProvider.NewInstanceFactory.getInstance();
}
HasDefaultViewModelProviderFactory在ComponentActivity中最终也是根据Application来判断是通过NewInstanceFactory或AndroidViewModelFactory来创建ViewModel实例。
如果要使用带参数的ViewModel实例,需要我们自定义Factory。Factory的自定义在文末再进行介绍。
3.1.2 ViewModelStore
Factory是用来创建ViewModel,而ViewModelStore则是用来存储ViewModel
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
//ViewModel作为Value,进行存储
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());
}
//清除ViewModel。如果ViewModelStore拥有者(Activity/Fragment)销毁后不会重建,需调用此方法
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
ViewModelStore作用:将ViewModel作为Value存储到HashMap中。
Activity和Fragment都通过实现了ViewModelStoreOwner来获取ViewModelStore,这里以Activity为例
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
@Override
public ViewModelStore getViewModelStore() {
//activity还没关联Application,即不能在onCreate之前去获取viewModel
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;
}
void ensureViewModelStore() {
if (mViewModelStore == null) {
//如果存储器是空,就先尝试恢复最近一次配置变更时保存下来的数据
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
//如果lastNonConfigurationInstance不存在,就new一个
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
getViewModelStore()的主要内容:
- 如果
ViewModelStore为空,先尝试从NonConfigurationInstance从获取ViewModelStore实例 - 如果
NonConfigurationInstance不存在,就new一个ViewModelStore。
NonConfigurationInstances类
NonConfigurationInstances类是ComponentActivity的一个静态内部类,用来存储viewModelStore,其实例不会随着配置改变而消失或改变。
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
ViewModel对象不会随着资源配置变化导致Activity/Fragment的销毁重建而改变,其奥秘就在这里。
Activity在Destroy之前会调用retainNonConfigurationInstances()方法,该方法中会执行NonConfigurationInstances存储操作。
//Activity.java
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
.......
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
NonConfigurationInstances mLastNonConfigurationInstances;
//ComponentActivity.java
@Override
@Nullable
@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
......
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore; //存储了ViewModelStore
return nci;
}
retainNonConfigurationInstances()将Activity的viewModelStore对象存入到NonConfigurationInstances对象中
为什么资源配置变化不会影响到NonConfigurationInstances实例呢
看一下getLastNonConfigurationInstance()方法
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
mLastNonConfigurationInstances是怎么来的?看一下attach方法
final void attach(Context context, ActivityThread aThread,
NonConfigurationInstances lastNonConfigurationInstances,
......
){
......
mLastNonConfigurationInstances = lastNonConfigurationInstances;
......
}
Activity的attach方法会给mLastNonConfigurationInstances赋值,为Activity关联上下文环境,在ActivityThread的performLaunchActivity方法中调用。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken, r.shareableActivityToken);
......
}
lastNonConfigurationInstances是存在 ActivityClientRecord中的一个组件信息,而 ActivityClientRecord存在于ActivityThread的mActivities中
//ActivityThread.java
/**
* Maps from activity token to local record of running activities in this process.
*/
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
Activity销毁前会调用retainNonConfigurationInstances()方法,而该方法是在performDestroyActivity()被调用
/** Core implementation of activity destroy call. */
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); //Activity信息存储
......
}
ActivityThread 中的 ActivityClientRecord 是不受 activity 重建影响,则其lastNonConfigurationInstances也不受影响,相应的NonConfigurationInstances的viewModelStore不受影响,则其中存储的ViewModel自然也不受影响
3.1.3 get
回到ViewModel最初的入口ViewModelProvider(this)[UserModel::class.java]。
ViewModelProvider(this)获取了Factory实例,接下来要调用get()方法,这个方法需要传入Class对象,ViewModelProvider 需要拿到 Class 才能完成反射操作。
get()主要是通过 modelClass 来自动生成一个字符串 Key,并将参数转发给另外一个 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)
}
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
//从Map中获取ViewModel缓存
var viewModel = store[key]
//判断该viewModel对象是否可以转成该类
if (modelClass.isInstance(viewModel)) {
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody")
if (viewModel != null) {
// TODO: log a warning.
}
}
//无缓存,重新通过mFactory构建
viewModel = if (factory is KeyedFactory) {
factory.create(key, modelClass)
} else {
factory.create(modelClass)
}
//将ViewModel进行缓存
store.put(key, viewModel)
return viewModel
}
通过key从ViewModelStore里取ViewModel实例,如果取不到或者类型不符,则通过mFactory.create()方法来反射初始化ViewModel,并在返回初始化结果前将它存到 mViewModelStore 中,从而完成ViewModel的初始化流程
3.1.4 小结
ViewModel存储和获取部分内容简单总结为:
- 所有实例化的
ViewModel都缓存在ViewModelStore的封装对象中,其实质是一个HashMap ViewModelStore与具体的Controller(Activity/Fragment)绑定,与其生命周期一样长- 获取
ViewModel的实例过程委托给ViewModelProvider工具类,其包含一个创建ViewModel的工厂类Factory和一个对ViewModelStore的引用 - 获取
ViewModel时,会先从ViewModelStore中获取缓存的ViewModel,若没有缓存过则用Facotry实例化一个新的ViewModel并缓存 viewModelStore存储在NonConfigurationInstances类的mLastNonConfigurationInstances中,其是存在ActivityClientRecord中的一个组件信息,ActivityClientRecord是存在ActivityThread的mActivities中,由于ActivityThread不受Activity重建影响,由此类推,ViewModel不受Activity重建影响
3.2 ViewModel回收
要知道 ViewModel 是在何时回收的,那么就只要看 ViewModelStore 是在什么时候清空 HashMap 就可以。
ViewModel实例对象存储在ViewModelStore,则只要确认ViewModelStore中HashMap清理时机,即可确认其回收时机。
在 ComponentActivity 中调用了 ViewModelStore 的 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在收到ON_DESTROY事件,如果判断是由于资源配置变更导致的页面销毁,不会调用ViewModelStore的clear()方法;如果是正常退出或被系统杀死,则就会调用该clear()方法,清空所有缓存的ViewModel实例
四、自定义ViewModel
ViewModelProvider提供了两个Factory接口实现类
NewInstanceFactory:通过反射初始化包含无参构造函数的ViewModelAndroidViewModelFactory:通过反射初始化包含参数仅有一个且为Application类型的构造函数的ViewModel
如果需要通过其他类型的构造函数来初始化ViewModel,需要自己实现ViewModelProvider.factory接口完成初始化逻辑
class MyViewModel(val age: Int) : ViewModel() {
val nameLiveData = MutableLiveData<String>()
}
class MainActivity : AppCompatActivity() {
//创建myViewModelA
private val myViewModelA by lazy {
ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel(10) as T
}
}).get(
MyViewModel::class.java
).apply {
nameLiveData.observe(this@MainActivity, {
})
}
}
//创建myViewModelB
private val myViewModelB by lazy {
ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel(20) as T
}
}).get(
MyViewModel::class.java
).apply {
nameLiveData.observe(this@MainActivity, {
})
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.e("myViewModelA", myViewModelA.toString() + " age: " + myViewModelA.age)
Log.e("myViewModelB", myViewModelB.toString() + " age: " + myViewModelB.age)
}
}
/**打印结果**/
E/myViewModelA: github.leavesc.demo.MyViewModel@e24ac80 age: 10
E/myViewModelB: github.leavesc.demo.MyViewModel@e24ac80 age: 10
myViewModelA 和 myViewModelB 虽然入参不同,但其对应同一个内存地址,即最先初始化的ViewModel实例被缓存下来重复使用。这是因为二者默认对应的Key是一样的,所以初始化 myViewModelB时之间复用之前的缓存。
@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 {
factory.create(modelClass)
}
store.put(key, viewModel)
return viewModel
}
如果希望 myViewModelA 和 myViewModelB 对应不同的实例对象,那么就需要在初始化的时候主动为它们指定不同的 Key,这样它们就可以一起被存到 ViewModelStore 的 HashMap 中
private val myViewModelA by lazy {
ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel(10) as T
}
}).get(
"keyA", MyViewModel::class.java
).apply {
nameLiveData.observe(this@MainActivity, {
})
}
}
private val myViewModelB by lazy {
ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel(20) as T
}
}).get(
"keyB", MyViewModel::class.java
).apply {
nameLiveData.observe(this@MainActivity, {
})
}
}
E/myViewModelA: github.leavesc.demo.MyViewModel@e24ac80 age: 10
E/myViewModelB: github.leavesc.demo.MyViewModel@9abd6fe age: 20
五、常见问题
5.1 初始化陷阱
这里说的初始化陷阱,在自定义ViewModel部分提过,即ViewModel的缓存复用机制可能导致的使用问题。
class AViewModel() : ViewModel() {
override fun onCleared() {
super.onCleared()
Log.e("AViewModel", "onCleared")
}
}
class BViewMode : ViewModel() {
override fun onCleared() {
super.onCleared()
Log.e("BViewMode", "onCleared")
}
}
class MainActivity : AppCompatActivity() {
private val aViewModel by lazy {
ViewModelProvider(this).get(
"myKey", AViewModel::class.java
)
}
private val bViewModel by lazy {
ViewModelProvider(this).get(
"myKey", BViewMode::class.java
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.e("aViewModel", aViewModel.toString())
Log.e("bViewModel", bViewModel.toString())
Log.e("MainActivity", "onCreate")
}
}
/***打印结果***/
E/aViewModel: github.leavesc.demo.AViewModel@3c93503
E/AViewModel: onCleared
E/bViewModel: github.leavesc.demo.BViewMode@e24ac80
E/MainActivity: onCreate
上述代码打印结果的原因是 aViewModel 和 bViewModel 使用了同个key,但是又不是同一个ViewModel类型,导致初始化bViewModel 时,会将HashMap中的 aViewModel覆盖并回收旧值。
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();
}
}
}
ViewModel使用注意点:
对于不同类型的 ViewModel 实例,在初始化的时候不能指定相同的 Key