ViewModel概述
ViewModel是一个状态存储器,它的主要优势是可以缓存状态,让ViewModel中的数据不受Configuration Change的影响。这意味着当你切换页面,或者屏幕旋转的时候,不需要重新获取数据。
ViewModel的优势
我们先来看看屏幕旋转时遇到的问题,如果在AndroidManifest.xml中没有配置configChanges="orientation|screenSize",系统会销毁并重建Activity,我们一般使用onSaveInstanceState()方法保存数据,然后使用onCreate()中的Bundle恢复数据。但是这个方法只能恢复序列化的数据,如果要序列化的对象很复杂,序列化会占用大量内存。由于这个过程发生在主线程,耗时的序列化可能导致掉帧和页面卡顿,并且这个方法不能用于恢复Bitmap这种大容量的数据(IPC对Bundle有1M的限制)。
使用ViewModel可以解决这个问题,ViewModel可以缓存数据,不受Configuration Change的影响。ViewModel的优势主要有2个:
- 更便于保存数据。
- 更方便UI组件之间的通信。
更便于保存数据
我们通常在Activity的onCreate()方法中请求ViewModel,在旋转设备屏幕时,系统会多次调用onCreate()方法,而ViewModel从你首次请求ViewModel直到Activity执行onDestroy()方法期间一直存在。如下图左侧展示了旋转屏幕到最终退出页面,Activity经历的多个生命周期状态,右侧是对应ViewModel的生命周期:
Activity销毁后,ViewModel的onCleared()方法才会最终执行释放ViewModel。它不会像Activity那样反复重建,从而节省了用于状态维护(数据的存储和获取、序列化和反序列化)的代码。
ViewModel在其生命周期内类似一个单例,这就引发了一个更好用的特性,那就是UI组件间的通信。
更方便UI组件之间的通信
由于Activity销毁后,ViewModel中的资源才会释放,就可以很方便地运用在一个Activity中有多个Fragment的场景,多个Fragment可以持有同一个ViewModel的实例,再结合LiveData,这也就意味着数据状态的共享。
比如下面这个示例:
java
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
}
}
xml
//activity_my.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<fragment
android:name="com.example.test.viewmodel.ListFragment"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<fragment
android:name="com.example.test.viewmodel.DetailFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@+id/fragment_list"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
java
public class ShareViewModel extends ViewModel {
private MutableLiveData<String> selected = new MutableLiveData<>();
public void setSelectedItem(String item){
selected.setValue(item);
}
public LiveData<String> getSelectedLiveData() {
return selected;
}
}
java
public class ListFragment extends Fragment {
private ShareViewModel shareViewModel;
private List<String> list = new ArrayList<>();
private RecyclerView.Adapter adapter;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list, container, false);
RecyclerView recyclerView = view.findViewById(R.id.rv);
LinearLayoutManager layoutManager = new LinearLayoutManager(this.getContext());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
adapter = new RecyclerView.Adapter<ItemViewHolder>() {
@NonNull
@Override
public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ItemViewHolder(new TextView(parent.getContext()));
}
@Override
public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {
String s = list.get(position);
if(holder.itemView instanceof TextView){
((TextView)holder.itemView).setText(s);
(holder.itemView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
shareViewModel.setSelectedItem(s);
}
});
}
}
@Override
public int getItemCount() {
return list.size();
}
};
recyclerView.setAdapter(adapter);
for(int i = 0; i < 100; i++){
list.add("item " + i);
}
adapter.notifyDataSetChanged();
return view;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
shareViewModel = new ViewModelProvider(requireActivity()).get(ShareViewModel.class);
}
static class ItemViewHolder extends RecyclerView.ViewHolder{
TextView tv;
public ItemViewHolder(@NonNull TextView itemView) {
super(itemView);
tv = itemView;
}
}
}
java
public class DetailFragment extends Fragment {
private ShareViewModel shareViewModel;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_detail, container, false);
TextView tv = view.findViewById(R.id.tv);
shareViewModel.getSelectedLiveData().observe(getViewLifecycleOwner(), new Observer<String>() {
@Override
public void onChanged(String s) {
tv.setText(s);
}
});
return view;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
shareViewModel = new ViewModelProvider(requireActivity()).get(ShareViewModel.class);
}
}
ListFragment和DetailFragment的requireActivity()方法返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个ViewModel。再结合LiveData,把LiveData放到ViewModel中,这样在ListFragment中点击了某个item,DetailFragment马上就可以监听到并显示相应的字符串,如下图所示:
源码分析
ViewModel的核心就是因配置更改,系统重建Activity或Fragment后,ViewModel的实例仍然存在,这是怎么实现的呢?下面我们就通过源码来进行分析。
自定义ViewModel需要继承ViewModel类,我们先来看看ViewModel类的代码:
java
public abstract class ViewModel {
private volatile boolean mCleared = false;
//clear()方法中会调用onCleared(),当ViewModel观察了一些数据,可以使用这个方法
//清除订阅来避免ViewModel的内存泄露
protected void onCleared() {
}
@MainThread
final void clear() {
mCleared = true;
...
onCleared();
}
}
ViewModel类是一个抽象类,内部逻辑比较简单。onCleared()方法是一个空方法,可以重写这个方法清除订阅来避免ViewModel的内存泄露
获取ViewModel的实例前我们先要获取ViewModelProvider实例,一般我们在Activity中使用new ViewModelProvider(this)
来获取ViewModelProvider实例:
java
public class ViewModelProvider {
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
//一个参数ViewModelStoreOwner
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
}
可以看到this实际上是ViewModelStoreOwner,它是一个接口:
java
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
在ComponentActivity中对这个接口进行了实现:
java
public class ComponentActivity implements ViewModelStoreOwner{
private ViewModelStore mViewModelStore;
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.");
}
ensureViewModelStore();
return mViewModelStore;
}
}
创建ViewModelProvider实例最终走的是ViewModelProvider类的第3个构造方法,其中ViewModelStore为ViewModel的存储器,Factory为创建ViewModel的工厂。ViewModelStore来自ComponentActivity中的getViewModelStore()
方法,Factory来自NewInstanceFactory.getInstance()
。
我们先来看看如何从ViewModelStore中获取ViewModel,获取ViewModel调用了ViewModelProvider的get()方法:
java
public class ViewModelProvider {
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
//获取canonicalName,其中包含包路径和类名
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//通过key从mViewModelStore中获取viewModel
ViewModel viewModel = mViewModelStore.get(key);
//判断viewModel是否是modelClass或其子类的实例
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 {
//没有获取到,使用mFactory创建
viewModel = mFactory.create(modelClass);
}
//存入mViewModelStore
mViewModelStore.put(key, viewModel);
//返回
return (T) viewModel;
}
}
从上面的代码可以看到,ViewModel以key-value的形式存储在mViewModelStore中,如果mViewModelStore中找不到ViewModel,就使用mFactory创建,然后再存入mViewModelStore。
mFactory来自NewInstanceFactory.getInstance(),NewInstanceFactory代码如下:
java
public class ViewModelProvider {
public static class NewInstanceFactory implements Factory {
private static NewInstanceFactory sInstance;
@NonNull
static NewInstanceFactory getInstance() {
if (sInstance == null) {
sInstance = new NewInstanceFactory();
}
return sInstance;
}
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
//直接通过newInstance()创建类的实例
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中找不到ViewModel,会直接反射创建ViewModel的实例存入mViewModelStore。
ViewModelStore类的代码如下:
java
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
//返回旧的ViewModel
ViewModel oldViewModel = mMap.put(key, viewModel);
//旧的ViewModel不为空,调用其onCleared()方法
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() {
//遍历ViewModel并调用其clear()方法
for (ViewModel vm : mMap.values()) {
vm.clear();
}
//mMap清空
mMap.clear();
}
}
ViewModelStore中使用HashMap存储ViewModel,由此可知,只要ViewModelStore不变,其中存储的ViewModel就不会变。
前面我们看到getViewModelStore()方法来自ComponentActivity:
java
public class ComponentActivity implements ViewModelStoreOwner{
private ViewModelStore mViewModelStore;
public ViewModelStore getViewModelStore() {
//判断getApplication()是否为null
//给mApplication赋值在Activity的attach()方法中,由此可知
//获取ViewModelStore需要在attach()方法之后
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) {
//从NonConfigurationInstances中获取ViewModelStore
mViewModelStore = nc.viewModelStore;
}
//没有获取到,新建一个
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
}
当我们调用getViewModelStore()获取ViewModelStore时,先会去getLastNonConfigurationInstance()中获取NonConfigurationInstances:
java
public class Activity{
NonConfigurationInstances mLastNonConfigurationInstances;
final void attach(NonConfigurationInstances lastNonConfigurationInstances){
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
}
getLastNonConfigurationInstance()方法来自Activity,给mLastNonConfigurationInstances赋值在Activity的attach()方法中。
这里先不管,第一次调用getViewModelStore()时,mViewModelStore为null,新建一个ViewModelStore赋值给mViewModelStore。
mViewModelStore存储在哪里呢?
在onRetainNonConfigurationInstance()方法中,会将mViewModelStore存入NonConfigurationInstances,顾名思义,非配置实例,即不受配置更改影响的实例。这里NonConfigurationInstances是ComponentActivity的静态内部类:
java
public class ComponentActivity{
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
@Override
@Nullable
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;
}
//将viewModelStore存入NonConfigurationInstances,返回
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
}
onRetainNonConfigurationInstance()方法又是在哪里调用的呢?
在这里添加断点,旋转屏幕,可以看到调用堆栈信息,如下:

Activity因配置更改而重建时,系统将执行Relaunch流程,系统在这个过程中通过同一个ActivityClientRecord来完成信息传递,会销毁当前Activity,紧接着再马上重建同一个Activity。我们先来看看handleRelaunchActivity()方法:
java
public final class ActivityThread{
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
@Override
public void handleRelaunchActivity(ActivityClientRecord tmp,
PendingTransactionActions pendingActions) {
...
//从mActivities中获取ActivityClientRecord
ActivityClientRecord r = mActivities.get(tmp.token);
...
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
}
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
PendingTransactionActions pendingActions, boolean startsNotResumed,
Configuration overrideConfig, String reason) {
...
//这里调用Activity的onPause()方法
performPauseActivity(r, false, reason, null /* pendingActions */);
...
//这里调用Activity的onStop()方法
callActivityOnStop(r, true /* saveState */, reason);
...
//这里调用Activity的onDestroy()方法
handleDestroyActivity(r.token, false, configChanges, true, reason);
...
//这里调用Activity的attach()方法并传入ActivityClientRecord
handleLaunchActivity(r, pendingActions, customIntent);
...
}
}
handleRelaunchActivity()方法从mActivities中获取ActivityClientRecord然后传给handleRelaunchActivityInner()方法,handleRelaunchActivityInner()方法中顺序调用了performPauseActivity()、callActivityOnStop()、handleDestroyActivity()、handleLaunchActivity(),其中handleLaunchActivity()方法中会调用Activity的attach()方法。
这里handleDestroyActivity()方法中又调用了performDestroyActivity()方法:
java
public final class ActivityThread{
@Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
//调用performDestroyActivity()方法
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance, reason);
...
}
}
performDestroyActivity()方法代码如下:
java
public final class ActivityThread{
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
...
if (getNonConfigInstance) {
try {
//在这里调用Activity的retainNonConfigurationInstances()方法,
//获取NonConfigurationInstances存入r中
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
...
}
}
}
...
return r;
}
}
在performDestroyActivity()中,通过Activity的retainNonConfigurationInstances()方法获取Activity
的NonConfigurationInstances
赋值给r.lastNonConfigurationInstances
,就相当于修改了mActivities
中对应的的ActivityClientRecord
。
而上面handleLaunchActivity()
方法传入ActivityClientRecord
跟这里是同一个,也就导致handleLaunchActivity()方法传入的ActivityClientRecord也同步修改了。
Activity的retainNonConfigurationInstances()方法代码如下:
java
public class Activity{
//注意这里的NonConfigurationInstances与ComponentActivity中的不一样
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
NonConfigurationInstances retainNonConfigurationInstances() {
//ComponentActivity是其子类,调用ComponentActivity的
//onRetainNonConfigurationInstance()方法
Object activity = onRetainNonConfigurationInstance();
...
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;
}
}
由于ComponentActivity是Activity的子类,retainNonConfigurationInstances()方法调用了ComponentActivity的onRetainNonConfigurationInstance()方法,将ComponentActivity
中的NonConfigurationInstances
赋值给Activity
的NonConfigurationInstances
中的activity
,注意这两个NonConfigurationInstances是不一样的。
总结一下:
大致流程是这样的,ViewModel存储在ViewModelStore中,Activity因配置更改销毁时,先将ViewModelStore存入ComponentActivity的NonConfigurationInstances,然后又把这个NonConfigurationInstances存入了Activity的NonConfigurationInstances,再存入ActivityClientRecord,最后存入ActivityThread的ArrayMap(mActivities)中。Activity重建时,调用Activity的attach()方法,传入的是同一个ActivityClientRecord,以后每次调用getViewModelStore()方法取的都是这里存储的ViewModelStore,这就是ViewModel不受配置更改影响的原因。
什么时候会清理ViewModelStore中的数据呢?
在Activity中,非配置变更触发的销毁会清除ViewModelStore中的数据:
java
public class ComponentActivity{
public ComponentActivity() {
...
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
mContextAwareHelper.clearAvailableContext();
//判断非配置变更,清理ViewModelStore
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
...
}
}
使用onSaveInstanceState()方法保存数据与使用ViewModel保存数据的区别:
-
onSaveInstanceState()是保存到Bundle中,只能保存Bundle能接收的数据类型,包括一些基本类型的数据和序列化的类型,而ViewModel可以保存任意类型的数据。
-
onSaveInstanceState()受到Binder事务缓冲区大小限制,只可以存储小容量的数据。ViewModel可以存储大容量的数据,但是会受到App内存空间的限制。
-
onSaveInstanceState()数据最终存储到ActivityManagerService的ActivityRecord中了,也就是存到系统进程中去了,所以App被杀之后还是能恢复。而ViewModel数据是存储到ActivityClientRecord中,也就是存到应用本身的进程中了,App被杀后没法恢复的。