ViewModel是以生命周期的方式存储和管理界面相关的数据。当系统销毁或重新创建Activity/Fragment的时候,那么存储在其中的数据都会消失,对于简单的数据,Activity可以通过onSaveInstanceState()方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,ViewModel的出现,正好弥补了这一个不足。
另一个问题,Activity/Fragment 经常需要进行一些异步调用,并确保在onDestroy要清理这些调用以避免潜在的内存泄漏。这就需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为可能需要重新发出已经发出过的调用。而ViewModel可以将该部分工作解耦出来,并且通过Lifecycle跟LiveData配合使用,可以有效避免内存泄漏。
一:ViewModel的使用
1.1添加依赖
在app的build.gradle里添加如下依赖:
def lifecycle_version = "2.2.0"
// 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"
1.2 实现ViewModel
class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData().also {
loadUsers()
}
}
fun getUsers(): LiveData<List<User>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
定义一个类继承ViewModel,ViewModel主要负责为界面准备数据,所以我们一般会在ViewModel里使用LiveData去承载数据。 如上有个用户列表users变量
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val model=ViewModelProvider(this).get(MyViewModel::class.java)
model.getUsers().observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}
在Activity中去使用用户列表。
- val model=ViewModelProvider(this).get(MyViewModel::class.java) 通过ViewModelProvider去创建ViewModel对象,接着就可以使用ViewModel里面的liveData了 注意:ViewModel 绝不能引用视图View、Lifecycle 或可能存储对 Activity 上下文的引用的任何类,避免存在内存泄漏。\
如果是继承AndroidViewModel的,那么需要传入Factory去创建ViewModel,代码如下
class RoomViewModel(val context:Application) :AndroidViewModel(context){
var users = MutableLiveData<List<User>>()
}
对应的Activity的代码如下:
class RoomTestActivity :AppCompatActivity(){
lateinit var viewModel:RoomViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
viewModel=ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory(application)).get(RoomViewModel::class.java)
...
}
}
如果ViewModel是继承AndroidViewModel,那么在通过ViewModelProvider创建对象的时候,需要去指定由ViewModelProvider.AndroidViewModelFactory创建。
二:ViewModel是如何创建的
以上就是ViewModel的基本使用,上面我们看到val model=ViewModelProvider(this).get(MyViewModel::class.java) ViewModel是通过这句代码创建出的实例,接着我们具体分析一下,ViewModel是如何创建的。
2.1 先创建ViewModelProvider
先来看看ViewModelProvider(this)这个代码
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
-
我们方法ViewModelProvider的构造函数需要传入的参数ViewModelStoreOwner,而我们发现我们的Activity继承 ComponentActivity而ComponentActivity刚好实现了ViewModelStoreOwner
public class ComponentActivity extends androidx.core.app.ComponentActivity implements LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner, OnBackPressedDispatcherOwner { ... }
-
owner.getViewModelStore()根据上面分析,owner其实就是ComponentActivity,所以我们看ComponentActivity的getViewModelStore方法
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) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; }
这里我们发现其实就是返回一个ViewModelStore对象。如果NonConfigurationInstances存在viewModelStore就使用NonConfigurationInstances存储的,如果没有就new一个。具体看看NonConfigurationInstances是啥
static final class NonConfigurationInstances { Object custom; ViewModelStore viewModelStore; }
NonConfigurationInstances就是一个静态对象,里面包含一个ViewModelStore,我们再来看看ViewModelStore
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(); } } final ViewModel get(String key) { return mMap.get(key); } Set<String> keys() { return new HashSet<>(mMap.keySet()); } public final void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } }
ViewModelStore其实是里面有个存储viewModel的HashMap,key是String,value是ViewModel。 所以总结一下:owner.getViewModelStore()其实就是创建了一个ViewModelStore对象。
-
owner instanceof HasDefaultViewModelProviderFactory ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory() : NewInstanceFactory.getInstance() 如果owner是HasDefaultViewModelProviderFactory,那么就调用HasDefaultViewModelProviderFactory的getDefaultViewModelProviderFactory()方法,否则就调用NewInstanceFactory.getInstance()方法。由于没有找到实现HasDefaultViewModelProviderFactory的地方,所以我们其实代码走的是NewInstanceFactory.getInstance()具体代码
static NewInstanceFactory getInstance() { if (sInstance == null) { sInstance = new NewInstanceFactory(); } return sInstance; }
我们看到就是new了一个NewInstanceFactory对象。 总结一下ViewModelProvider(this)做了什么事情
-
创建一个ViewModeStore,创建一个Factory,根据不同的类型有可能是创建的是NewInstanceFactory
-
把ViewModeStore赋值给全局变量mViewModelStore,把创建出来的Factory复制给全局变量mFactory
2.2 创建ViewModel
接着看get(MyViewModel::class.java)其实就是ViewModelProvider的get方法。
private static final String DEFAULT_KEY ="androidx.lifecycle.ViewModelProvider.DefaultKey";
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
接着看get(DEFAULT_KEY + ":" + canonicalName, modelClass);
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
- ViewModel viewModel = mViewModelStore.get(key); 我们可以看到从ViewModelStore中去获取ViewModel
- key是DEFAULT_KEY:+传入的viewmode的名字
- 如果有拿到就返回,如果没有拿到就通过viewModel = (mFactory).create(modelClass);Factory的create方法去创建。我们知道之前跟到的Factory是NewInstanceFactory,所以来看看NewInstanceFactory的create方法
通过 modelClass.newInstance();通过反射的方式直接new了一个ViewModel对象。public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { //noinspection TryWithIdenticalCatches try { return modelClass.newInstance(); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } }
- mViewModelStore.put(key, viewModel);创建完成之后再键值对形式存储到ViewModelStore中。
三:ViewModel是如何在Activity异常销毁重建的时候保持数据不变的
通过上面我们知道了,ViewModel是存储在ViewModelStore中的,而每个Activity都有个ViewModelStore,如果ViewModelStore没有被重新创建,那么ViewModel就不会被重新创建。而通过上面我们也知道ViewModelStore是存储在NonConfigurationInstances类中,NonConfigurationInstances是ComponentActivity里的一个静态内部类,那何时去存储到NonConfigurationInstances类中的呢?我们如下分析: 首先状态得保存下来,才能在重建的时候取出来。既然是要保存状态,那肯定是在onDestroy()的时候保存的,所以直接来看看ActivityThread$performDestroyActivity()
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
...
if (getNonConfigInstance) {
try {
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);
}
}
}
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnDestroy(r.activity);
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + safeToComponentShortString(r.intent) +
" did not call through to super.onDestroy()");
}
if (r.window != null) {
r.window.closeAllPanels();
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to destroy activity " + safeToComponentShortString(r.intent)
+ ": " + e.toString(), e);
}
}
r.setState(ON_DESTROY);
}
...
return r;
}
关键代码r.activity.retainNonConfigurationInstances();来具体看看
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
// We're already stopped but we've been asked to retain.
// Our fragments are taken care of but we need to mark the loaders for retention.
// In order to do this correctly we need to restart the loaders first before
// handing them off to the next activity.
mFragments.doLoaderStart();
mFragments.doLoaderStop(true);
ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
if (activity == null && children == null && fragments == null && loaders == null
&& mVoiceInteractor == null) {
return null;
}
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的实现如下:
public final Object onRetainNonConfigurationInstance() {
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;
}
所以我们 nci.viewModelStore = viewModelStore;看到ViewModelStore是在这个地方被存储到NonConfigurationInstances对象中的。那么什么时候会把存储的数据再拿过来用呢?我们来看看Activity的attach方法。
public final Activity startActivityNow(Activity parent, String id,
Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
Activity.NonConfigurationInstances lastNonConfigurationInstances, IBinder assistToken) {
ActivityClientRecord r = new ActivityClientRecord();
...
r.lastNonConfigurationInstances = lastNonConfigurationInstances;
...
return performLaunchActivity(r, null /* customIntent */);
}
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);
...
return activity;
}
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) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
}
...
}
我们看到NonConfigurationInstances对象保存在ActivityClientRecord中,然后在重启Activity的onAttach()方法中将NonConfigurationInstances拿回来,从而实现了数据的不丢失。
四:ViewModel是何时回收的
我们可以看到在创建ComponentActivity的时候,会添加一个Lifecycle的Observer。如下:
public ComponentActivity() {
...
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
...
}
在接收到Lifecycle.Event.ON_DESTROY,onDestroy的事件时候,会去调用 getViewModelStore().clear();方法。getViewModelStore我们知道其实就是ViewModeStore,clear方法如下:
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
其实就是遍历map,把数据清空。
五:ViewModel的生命周期
ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。下面提供一张官网的图,可以很清晰的看到生命周期