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 ,这也就意味着数据状态的共享。
比如下面这个示例:
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
}
}
activity_my.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/fragment_list"
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:id="@+id/fragment_detail"
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>
ShareViewModel 用于共享数据:
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;
}
}
ListFragment用于展示列表:
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);
// item 被点击后通知 shareViewModel
(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;
}
}
}
DetailFragment用于展示详情页:
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 类的代码:
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 的内存泄露
前面的代码中,通过new ViewModelProvider(requireActivity()).get(ShareViewModel.class)可以获取到 ShareViewModel 的实例,先来看看ViewModelProvider(requireActivity()):
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;
}
}
可以看到 ViewModelProvider 的构造函数传入的参数是 ViewModelStoreOwner,它是一个接口:
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
在 ComponentActivity 中对这个接口进行了实现:
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;
}
}
类似自定义 View ,创建 ViewModelProvider 实例最终都会调用 ViewModelProvider 类的第 3 个构造方法,其中 ViewModelStore 为 ViewModel 的存储器,Factory 为创建 ViewModel 的工厂。
ViewModelStore 来自 ComponentActivity 中的 getViewModelStore() 方法,Factory 来自NewInstanceFactory.getInstance()。
我们先来看看如何从 ViewModelStore 中获取 ViewModel,获取 ViewModel 调用了 ViewModelProvider 的get(@NonNull Class<T> modelClass)方法:
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 代码如下:
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类的代码如下:
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:
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) {
// 先会去 getLastNonConfigurationInstance() 中获取 NonConfigurationInstances
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// 从 NonConfigurationInstances 中获取 ViewModelStore
mViewModelStore = nc.viewModelStore;
}
// 没有获取到,新建
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
}
当我们调用 getViewModelStore() 获取 ViewModelStore 时,先会去 Activity 的getLastNonConfigurationInstance() 中获取 NonConfigurationInstances,拿到里面存储的viewModelStore,如果没有,就 new ViewModelStore()。getLastNonConfigurationInstance() 代码如下:
public class Activity {
NonConfigurationInstances mLastNonConfigurationInstances;
final void attach(NonConfigurationInstances lastNonConfigurationInstances) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
}
由此可知,ComponentActivity 的 NonConfigurationInstances 来自 Activity 的mLastNonConfigurationInstances.activity,mLastNonConfigurationInstances 赋值在 Activity 的 attach() 方法中。
这里先不管,第一次调用 getViewModelStore() 时,mViewModelStore 为 null,新建一个 ViewModelStore 赋值给 mViewModelStore,但是上面并没有看到把 mViewModelStore 存入 ComponentActivity 的 NonConfigurationInstances 的代码。
把 mViewModelStore 存入 ComponentActivity 的 NonConfigurationInstances 在onRetainNonConfigurationInstance() 方法中,这里会将 mViewModelStore 存入NonConfigurationInstances,顾名思义,非配置实例,即不受配置更改影响的实例。这里NonConfigurationInstances 是 ComponentActivity 的静态内部类:
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 存入 ComponentActivity 的 NonConfigurationInstances 并返回
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
}
onRetainNonConfigurationInstance() 方法又是在哪里调用的呢?
在这里添加断点,旋转屏幕,可以看到调用堆栈信息,如下:

Activity 因配置更改而重建时,系统将执行Relaunch流程,系统在这个过程中通过同一个 ActivityClientRecord 来完成信息传递,会销毁当前 Activity ,紧接着再马上重建同一个 Activity 。我们先来看看 handleRelaunchActivity() 方法:
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() 方法:
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() 方法代码如下:
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() 方法代码如下:
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 中的数据:
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 被杀后没法恢复。