一、ViewModel存在的意义?
ViewModel做为JetPack中重要的组件,翻译成中文便是“视图模型”,根据分离关注点准则,ViewModel的呈现,主要是为了分担Activity中的责任,专门用于寄存和界面相关的数据。只要是在界面上能看到的数据,它的相关变量都应该寄存在ViewModel中,而不是Activity中。 假如ViewModel效果仅仅将本来寄存在Activity/Fragment中的数据拆分出去,那岂不是跟一般的Java类没啥两样? 其实ViewModel更重要的是:
- 完成数据同享和跨模块通信,ViewModel的存储是以Activity的class为key寄存的,就可在有Activity上下文的不同子模块获取同享的ViewModel目标;
- Configuration改变后Activity重建后将毁掉前的数据再attach到视图中;
- ViewModel中提供了生命周期感知(LiveData+Lifecycle)和协程(ViewModel自身定义了ViewModelScope协程效果域)的支持;
二、怎么创立?
第一种办法(已弃用,不主张运用):ViewModelProviders.of().get()
val viewModel1 = ViewModelProviders.of(this).get(TestClearViewModel::class.java)
该办法现已被官方弃用,不再主张运用,其实跟踪ViewModelProviders.of()办法完成,内部便是new了一个ViewModelProvider目标。

第二种办法(第一种计划衍生出来的):自己创立ViewModelProvider实例,调用其get()办法,传入ViewModel的class目标
获取代码
val viewModel = ViewModelProvider(this).get(TestClearViewModel::class.java)
坏处
在同一个Activity的上下文,在不同的模块(类,即便同一个功能模块也会按单一责任拆分代码)中,想要获取同享的ViewModel实例会new多个ViewModelProvider()目标。

第三种办法(kt托付):
val viewModel3 by viewModels<TestClearViewModel>()
运用该办法获取ViewModel实例,需求添加以下依靠:
// ps: 版别选取合适自己项目的
implementation 'androidx.activity:activity-ktx:1.6.0'
完成代码:
@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline extrasProducer: (() -> CreationExtras)? = null,
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return ViewModelLazy(
VM::class,
{ viewModelStore },
factoryPromise,
{ extrasProducer?.invoke() ?: this.defaultViewModelCreationExtras }
)
}
终究完成还是经过new一个ViewModelProvider传入ViewModelProvider.Factory目标的办法创立的ViewModel目标,不同的是经过kt的Lazy机制,完成了一层缓存逻辑;
public class ViewModelLazy<VM : ViewModel> @JvmOverloads constructor(
private val viewModelClass: KClass<VM>,
private val storeProducer: () -> ViewModelStore, // 由Activity、Fragment完成ViewModelStoreOwner接口后提供
private val factoryProducer: () -> ViewModelProvider.Factory,
private val extrasProducer: () -> CreationExtras = { CreationExtras.Empty }
) : Lazy<VM> {
private var cached: VM? = null
override val value: VM
get() {
// 其实在ViewModelStore中现已有一层HashMap完成的缓存了,这里又增加一层缓存。
val viewModel = cached
return if (viewModel == null) {
val factory = factoryProducer()
val store = storeProducer()
// 中心完成还是ViewModelProvider结构办法中传入ViewModelProvider.Factory目标
ViewModelProvider(
store,
factory,
extrasProducer()
).get(viewModelClass.java).also {
cached = it
}
} else {
viewModel
}
}
override fun isInitialized(): Boolean = cached != null
}
- 从上面的代码中看到viewModels()办法是做为ComponentActivity的拓宽办法完成的;
- 懒加载按需创立ViewModel实例
- ViewModelProvider.Factory运用的是ComponentActivity中的默许完成defaultViewModelProviderFactory
三、生命周期?什么时候被毁掉?
网上许多关于ViewModel的说法,有相似“ViewModel的生命周期是善于Activity的”这样的说法,其实是简单让人产生误解的。 下面进行测验验证一下,理解一下ViewModel的存活周期。 测验代码很简单,在Activity中创立一个自定义的ViewModel
// TestClearViewModel.kt的完成:
class TestClearViewModel : ViewModel() {
var userId = MutableLiveData<String>()
override fun onCleared() {
super.onCleared()
Log.d(MainActivity2.TAG, "onCleared: $this")
}
}
// MainActivity2.kt的完成代码:
class MainActivity2 : AppCompatActivity() {
companion object {
const val TAG = "MainActivity_TAG"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val viewModel by viewModels<TestClearViewModel>()
Log.d(TAG, "onCreate viewModel: $viewModel")
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
Log.d(TAG, "onConfigurationChanged: ")
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy: ")
}
}
- case1 : 正常的按back键finish掉Activity,再重建Activity:
// 按back键之后的log:
com.yanggui.mvvmdemo2 D/MainActivity_TAG: onCreate viewModel: com.yanggui.mvvmdemo2.TestClearViewModel@86439b1
com.yanggui.mvvmdemo2 D/MainActivity_TAG: onCleared: com.yanggui.mvvmdemo2.TestClearViewModel@86439b1
com.yanggui.mvvmdemo2 D/MainActivity_TAG: onDestroy:
// 重现点击桌面图标,重启Activity之后的log:
com.yanggui.mvvmdemo2 D/MainActivity_TAG: onCreate viewModel: com.yanggui.mvvmdemo2.TestClearViewModel@8e96463
从上面的log能够看到,ViewModel是跟从Activity的毁掉而毁掉的,Activity重启之后ViewModel也会新建一个实例。
- case2 : 旋转屏幕之后finish掉Activity,引起的Activity重建:
// Activity在点击桌面图标,应用启动时创立的ViewModel实例:
com.yanggui.mvvmdemo2 D/MainActivity_TAG: onCreate viewModel: com.yanggui.mvvmdemo2.TestClearViewModel@c26ae96
// 第一次旋转屏幕引起Activity重建:
com.yanggui.mvvmdemo2 D/MainActivity_TAG: onDestroy:
com.yanggui.mvvmdemo2 D/MainActivity_TAG: onCreate viewModel: com.yanggui.mvvmdemo2.TestClearViewModel@c26ae96
// 第2次旋转屏幕引起Activity重建:
com.yanggui.mvvmdemo2 D/MainActivity_TAG: onDestroy:
com.yanggui.mvvmdemo2 D/MainActivity_TAG: onCreate viewModel: com.yanggui.mvvmdemo2.TestClearViewModel@c26ae96
经过上述实践验证,旋转屏幕其实ViewModel不会重建。
根据上述实践代码验证,也能阐明google官方这张流程图:

-
- 正常情况的onDestroy,ViewModel会跟从Activity毁掉而毁掉;
-
- 反常情况下的onDestroy,ViewModel不会跟从Activity毁掉而毁掉,因为要在Activity反常毁掉之后重建时用来根据ViewModel的数据恢复界面;
3.1 旋转屏幕之后ViewModel为什么不会重建?
ViewModel是由ViewModelStore类办理的,在其内部保护了一个HashMap,key是androidx.lifecycle.ViewModelProvider.DefaultKey:+ ViewModel的Class目标的canonicalName拼接而成的,value便是ViewModel目标。
// ViewModelStore.java:
public class ViewModelStore {
// ViewModelStore存储多个ViewModel实例,用HashMap寄存
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());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
- ViewModelStore和ViewModelStoreOwner的关系
ViewModelStoreOwner其实便是常见的Fragment、ComponentActivity(完成了ViewModelStoreOwner)
- ComponentActivity#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) {
//(1)
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
//(2)
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
//(3)
// 直接new了一个ViewModelStore用户办理一个ViewModel Map调集
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
(1)这一步很要害,mViewModelStore用来办理ViewModel的map调集,可是假如mViewModelStore仅仅作为ComponentActivity的一个成员变量,那Activity毁掉重建后,mViewModelStore也一定会毁掉重建。在(3)下面的代码中也能够看到mViewModelStore是在getViewModelStore()中直接new出来的。
- Fragment#getViewModelStore(): 转调FragmentManager#getViewModelStore(),终究在FragmentManagerViewModel中保护了一个Map<String, ViewModelStore>,用于寄存多个Fragemnt对应的ViewModel实例,以who变量为key去获取ViweModel实例;
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
return mFragmentManager.getViewModelStore(this);
}
当ViewModel被毁掉时会回调其onCleared()办法,直接跟进调用点发现,在ComponentActivity的无参结构办法中有如下代码:
public ComponentActivity() {
// ...
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();
// 在这里做ViewModel的毁掉逻辑
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
// ...
}
从如上代码能够得出定论: 非反常情况(改变Configuration等)下的Activity毁掉,ViewModel是在Activity的onDestroy生命周期时毁掉。
3.2 isChangingConfigurations()的定义
// 以下代码是在Activity.java中定义的:
boolean mChangingConfigurations = false;
public boolean isChangingConfigurations() {
return mChangingConfigurations;
}
在Activity和Activity的子类中都没有找到有赋值的地方,因为修饰符是package的,于是在其相同包名下,经过Find in files查找到是在ActivityThread.java的handleRelaunchActivity()中赋值为true的。
// ActivityThread.java,sdk 33
@Override
public void handleRelaunchActivity(ActivityClientRecord tmp, PendingTransactionActions pendingActions) {
// ...
r.activity.mConfigChangeFlags |= configChanges;
r.mPreserveWindow = tmp.mPreserveWindow;
// 这里会在屏幕旋转Activity重建时执行,将activity.mChangingConfigurations赋值为true
r.activity.mChangingConfigurations = true;
// ...
}

以上仅仅验证了Activity.isChangingConfigurations()==true的办法不会调用getViewModelStore().clear()去清理当时Activity关联的ViewModel实例的HashMap调集。
屏幕旋转Activity会产生onDestroy()->onCreate重建的情况,重建的Activity是这么关联到之前的ViewModel的Map调集的?
3.3 定论
- 1、手机旋转屏幕时Activity会被重建,可是ViewModel实例不会从头创立,由此可见ViewModel的生命周期是善于Activity的仅在非正常退出的情况建立; ps:上述现象是清单文件中Activity节点不装备android:configChanges=”orientation|screenSize”特点的情况下会走onDestroy->onCreate流程,Activity被重建。
<activity
android:name=".MainActivity2"
android:exported="true"
android:configChanges="orientation|screenSize"> // 注:两个特点都要写上才会不走重建Activity
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- 2、只有当Activity正常退出时才会跟着Activity一起毁掉; 正常退出:非ConfigurationChange造成的Activity重建。