系列文章目录
一、jetpack系列之Lifecycle
二、jetpack系列之LiveData
三、 jetpack系列之ViewModel
jetpack系列之ViewModel
一、ViewModel是什么?
ViewModel的官方解释如下:
ViewModel 类是一种业务逻辑或屏幕级状态容器。它用于将状态公开给界面,以及封装相关的业务逻辑。 它的主要优点是,它可以缓存状态,并可在配置更改后持久保留相应状态。这意味着在 activity 之间导航时或进行配置更改后(例如旋转屏幕时),界面将无需重新提取数据。
ViewModel 类的主要优势实际上有两个方面:
- 它允许您持久保留界面状态。
- 它可以提供对业务逻辑的访问权限。
ViewModel可以提供一个自动的数据持久化的功能。在屏幕旋转,或Activity之间的跳转时,正常数据是会消失。通过ViewModel,依然可以获取到原来的数据。在ViewModel中,我们可以写一些业务逻辑,将数据在View层和Model层传递。
二、使用步骤
在此展现一下ViewModel的简单使用,后面与LiveData等结合使用:
- 新建一个ViewModel,继承ViewModel:
import androidx.lifecycle.ViewModel
class MyViewModel:ViewModel() {
private var number = 0
fun addNumber() {
this.number++
}
fun getNumber():Int {
return number
}
}
- 在Activity中引用:
import androidx.activity.viewModels
class ViewModelActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
private lateinit var myTextView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
myTextView = findViewById(R.id.number_text_view)
myTextView.text = viewModel.getNumber().toString()
}
fun addNumber(view: View) {
viewModel.addNumber()
}
}
addNumber是通过按钮点击,使MyViewModel中的number加1。如果不使用ViewModel,在屏幕旋转的时候,number不会被记
录,TextView中显示的依然是0.在ViewModel中,number则会保持增加后的数值。
三、ViewModel的生命周期
官方给的解释:
ViewModel 的生命周期与其作用域直接关联。ViewModel 会一直保留在内存中,直到其作用域ViewModelStoreOwner 消失。以下上下文中可能会发生这种情况:
对于 activity,是在 activity 完成时。
对于 fragment,是在 fragment 分离时。
对于Navigation 条目,是在 Navigation 条目从返回堆栈中移除时。
下图说明了 activity 经历屏幕旋转而后结束时所处的各种生命周期状态。该图还在关联的 activity 生命周期的旁边显示了 ViewModel 的生命周期。此图表说明了 activity 的各种状态。这些基本状态同样适用于 fragment 的生命周期。
简单理解,ViewModel的生命周期,就是在Activity创建的时候创建,销毁的时候销毁。
三、ViewModel原理
1、ViewModel的创建过程
看一下ViewModel是怎么实现的:
private val viewModel: MyViewModel by viewModels()
使用了委托机制,交给androidx.activity.viewModels处理。
@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 }
)
}
viewModels返回了ViewModelLazy, 通过by关键字懒加载。这里我们可以传入一个生产ViewModel的工厂,如果不传,就会使用默认的SavedStateViewModelFactory。还需要传入一个ViewModelStore。ViewModelStore维护了一个map,存放了所有的VIewModel。看一下VIewModelStore的获取过程:
//ComponentActivity.java
@NonNull
@Override
public ViewModelStore getViewModelStore() {
...
//确保ViewModelStore存在
ensureViewModelStore();
return mViewModelStore;
}
void ensureViewModelStore() {
if (mViewModelStore == null) {
//如果不存在则从NonConfigurationInstances 中获取,如果NonConfigurationInstances 中没有,则创建一个新的。
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
//Activity.java 获取NonConfigurationInstances 的过程
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
NonConfigurationInstances 维护了一个ViewModelStore:
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
ComponentActivity实现 ViewModelStoreOwner 接口。然后,ViewModel 的作用域将限定为 ViewModelStoreOwner 的 Lifecycle。它会一直保留在内存中,直到其 ViewModelStoreOwner 永久消失。
ViewModelStore非常重要,它是在确保Activity重新创建时,获取到原来ViewModel的关键。新的Activity创建的时候,会将老的Activity的NonConfigurationInstances 赋值给新的Activity,新的Activity才能获取到ViewModelStore,从而获取到ViewModel。
继续看ViewModel的创建过程,ViewModelLazy实现如下:
public class ViewModelLazy<VM : ViewModel> @JvmOverloads constructor(
private val viewModelClass: KClass<VM>,
private val storeProducer: () -> ViewModelStore,
private val factoryProducer: () -> ViewModelProvider.Factory,
private val extrasProducer: () -> CreationExtras = { CreationExtras.Empty }
) : Lazy<VM> {
private var cached: VM? = null
override val value: VM
get() {
val viewModel = cached
return if (viewModel == null) {
val store = storeProducer()
val factory = factoryProducer()
val extras = extrasProducer()
ViewModelProvider.create(store, factory, extras)
.get(viewModelClass)
.also { cached = it }
} else {
viewModel
}
}
override fun isInitialized(): Boolean = cached != null
}
VM是传入的我们自己的MyViewModel.使用ViewModelProvider获取到我们的ViewModel:
//ViewModelProvider.android.kt
@MainThread
public actual operator fun <T : ViewModel> get(modelClass: KClass<T>): T =
impl.getViewModel(modelClass)
//ViewModelProviderImpl.kt
@Suppress("UNCHECKED_CAST")
internal fun <T : ViewModel> getViewModel(
modelClass: KClass<T>,
key: String = ViewModelProviders.getDefaultKey(modelClass),
): T {
val viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
if (factory is ViewModelProvider.OnRequeryFactory) {
factory.onRequery(viewModel!!)
}
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody") if (viewModel != null) {
// TODO: log a warning.
}
}
val extras = MutableCreationExtras(extras)
extras[ViewModelProviders.ViewModelKey] = key
return createViewModel(factory, modelClass, extras).also { vm -> store.put(key, vm) }
}
internal actual fun <VM : ViewModel> createViewModel(
factory: ViewModelProvider.Factory,
modelClass: KClass<VM>,
extras: CreationExtras
): VM {
// Android targets using `compileOnly` dependencies may encounter AGP desugaring
// issues where `Factory.create` throws an `AbstractMethodError`. This is resolved by an
// Android-specific implementation that first attempts all `ViewModelProvider.Factory.create`
// method overloads before allowing the exception to propagate.
// (See b/230454566 and b/341792251 for more details).
return try {
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
try {
factory.create(modelClass.java, extras)
} catch (e: AbstractMethodError) {
factory.create(modelClass.java)
}
}
}
可以看到,使用了工厂创建的ViewModel,创建完成后,会将ViewModel存放在ViewModelStore中。ViewModelStore维护了一个Map,保存所有的ViewModel。这里最终会在SavedStateViewModelFactory中创建ViewModel:
//SavedStateViewModelFactory.kt
...
val viewModel: T = if (isAndroidViewModel && application != null) {
newInstance(modelClass, constructor, application!!, controller.handle)
} else {
newInstance(modelClass, constructor, controller.handle)
}
viewModel.addCloseable(
LegacySavedStateHandleController.TAG_SAVED_STATE_HANDLE_CONTROLLER, controller
)
return viewModel
}
internal fun <T : ViewModel?> newInstance(
modelClass: Class<T>,
constructor: Constructor<T>,
vararg params: Any
): T {
return try {
constructor.newInstance(*params)
} catch (e: IllegalAccessException) {
throw RuntimeException("Failed to access $modelClass", e)
} catch (e: InstantiationException) {
throw RuntimeException("A $modelClass cannot be instantiated.", e)
} catch (e: InvocationTargetException) {
throw RuntimeException(
"An exception happened in constructor of $modelClass", e.cause
)
}
}
最终是通过反射的方式创建了ViewModel。这里没有太复杂的逻辑。
2、ViewModel的销毁
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();
}
mReportFullyDrawnExecutor.activityDestroyed();
}
}
});
...
}
ComponentActivity创建的时候,就会在lifecycle状态为Lifecycle.Event.ON_DESTROY的时候,判断如果不是因为配置更改导致的Activity结束,则将ViewModelStore中的map数据清空。
3、ViewModel的数据恢复
这里涉及到了Activity的整个生命周期的执行逻辑,感兴趣的小伙伴可以自己学习一下。这里做简单介绍.
- 在Activity在destroy时候,首先将NonConfigurationInstances存放在ActivityClientRecord中:
//ActivityThread.java
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
...
if (getNonConfigInstance) {
try {
//保存NonConfigurationInstances
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException("Unable to retain activity "
+ r.intent.getComponent().toShortString() + ": " + e.toString(), e);
}
}
}
...
}
//r.activity.retainNonConfigurationInstances()的实现,创建了一个NonConfigurationInstances,并将Activity、Fragment等都保存进来。
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;
}
//onRetainNonConfigurationInstance(),在ComponentActivity.java中实现,保存了ViewModelStore:
@Override
@Nullable
@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
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.java 新的Activity启动,执行attach方法,将旧的NonConfigurationInstances传递过来:
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
至此,从NonConfigurationInstances 获取到ViewModelStore,再从ViewModelStore中获取ViewModel。ViewModel已经恢复。
总结
- ViewModel的生命周期,就是在Activity创建的时候开始,销毁的时候结束。
- 配置没有改变时候,Activity进行重建,会将旧Activity中的ViewModel传递给新的Activity。
- ViewModel将作用域限定为实现ViewModelStoreOwner 接口的对象中的Lifecycle。