一、为什么使用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的几个核心角色
-
ViewModelProvider
ViewModel
的使用工具类,封装了一系列作用方法,用于创建ViewModel
-
ViewModelStore
存储多个
ViewModel
的存储类 -
ViewModelStoreOwner
ViewModel
存储器的拥有者,Activity
、Fragment
都是其实现者 -
SavedStateHandle
ViewModel
的功能扩展,使得开发者无需直接使用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
:通过反射初始化包含无参构造函数的ViewModel
AndroidViewModelFactory
:通过反射初始化包含参数仅有一个且为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