跳到主要内容 Android MVVM 架构核心:ViewModel 详解与源码分析 | 极客日志
Java java
Android MVVM 架构核心:ViewModel 详解与源码分析 ViewModel 用于存储和管理 UI 相关数据,生命周期长于 Activity,适用于数据持久化、异步回调处理及 Fragment 间共享数据。通过 ViewModelProvider 和 ViewModelStore 管理实例,利用 HolderFragment 保持生命周期。使用时应避免持有 Context 引用以防内存泄漏,推荐使用 Application 上下文或 AndroidViewModel。现代开发建议结合协程与依赖注入优化实现。
ViewModel 类被设计用来以可感知生命周期的方式存储和管理 UI 相关数据。在 ViewModel 中,数据会一直存活,即使 Activity 配置发生变化(如屏幕旋转)。
ViewModel 有什么优势
1. 数据持久化
Activity 在销毁重建时,之前我们可以用 Activity 的 onSaveInstanceState() 机制保存和恢复数据,但缺点很明显:onSaveInstanceState 只适合保存少量的可以被序列化、反序列化的数据。假如我们需要保存一个比较大的 Bitmap List,这种机制明显不合适。ViewModel 就可以解决这种问题。
ViewModel 生命周期贯穿整个 Activity 生命周期,包括 Activity 因旋转造成的重创建,直到 Activity 真正意义上销毁后才会结束。
2. 异步回调问题
App 需要频繁异步请求数据,比如调接口请求数据。这些请求的回调都是相当耗时的,之前我们在 Activity 或 Fragment 里接收这些回调,不得不考虑潜在的内存泄漏情况,比如 Activity 被销毁后接口请求才返回。处理这些问题会给开发增添很多复杂的工作。现在利用 ViewModel 处理数据回调,可以很好地解决此问题。
3. 分担 UI Controller 负担
从最早的 MVC 到目前流行的 MVP、MVVM,目的无非是明确职责,分离 UI Controller 负担。UI Controller 比如 Activity、Fragment 是设计用来渲染展示数据、响应用户行为、处理系统的某些交互。如果再要求它去负责加载网络或数据,会让其显得臃肿和难以管理。为了简洁、清爽、丝滑,我们可以分离出数据操作的职责给 ViewModel。
4. Fragments 间共享数据
比如在一个 Activity 里有多个 Fragment,这 Fragment 之间需要做某些交互。之前的做法是接口回调,需要统一在 Activity 里管理,并且不可避免的 Fragment 之间还得互相持有对方的引用。
使用 ViewModel 时,Activity 与其内部的 Fragment 可以共用一个 ViewModel:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData <>();
public void select (Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected () {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
@Override
public void {
.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(v -> {
model.select(item);
});
}
}
{
{
.onCreate(savedInstanceState);
ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe( , item -> {
});
}
}
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
onCreate
(Bundle savedInstanceState)
super
public
class
DetailFragment
extends
Fragment
@Override
public
void
onCreate
(Bundle savedInstanceState)
super
SharedViewModel
model
=
this
Activity 不需要做任何事,甚至不知道这次交互,完美解耦。
Fragment 只需要与 ViewModel 交互,不需要知道对方 Fragment 的状态甚至是否存在,更不需要持有其引用。所有当对方 Fragment 销毁时,不影响本身任何工作。
Fragment 生命周期互不影响,甚至 Fragment 替换成其他的也不影响这个系统的运作。
用法简介 ViewModel 一般配合 LiveData 使用。
获取 ViewModel 实例,通过提供的类 ViewModelProviders:
MyViewModel model = ViewModelProviders.of(activity).get(MyViewModel.class);
MyViewModel model = ViewModelProviders.of(fragment).get(MyViewModel.class);
MyViewModel model = ViewModelProviders.of(activity, factory).get(MyViewModel.class);
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers () {
if (users == null ) {
users = new MutableLiveData <>();
loadUsers();
}
return users;
}
private void loadUsers () {
}
}
public class MyActivity extends AppCompatActivity {
@Override
public void onCreate (Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this ).get(MyViewModel.class);
model.getUsers().observe(this , users -> {
});
}
}
源码分析原理 ViewModel 一般在 onCreate 里初始化。
1. 实例化的代码 ViewModelProviders.of(activity, factory).get(MyViewModel.class)
1)首先是 ViewModelProviders 的 of 方法:
@MainThread
public static ViewModelProvider of (@NonNull FragmentActivity activity) {
initializeFactoryIfNeeded(checkApplication(activity));
return new ViewModelProvider (ViewModelStores.of(activity), sDefaultFactory);
}
@MainThread
public static ViewModelProvider of (@NonNull FragmentActivity activity,
@NonNull Factory factory) {
checkApplication(activity);
return new ViewModelProvider (ViewModelStores.of(activity), factory);
}
参数有 activity 与 fragment 的我就只贴 activity 的了,重点看这里引出了一个 Factory,不带 Factory 的方法只是通过 initializeFactoryIfNeeded 初始化了一个 sDefaultFactory(Factory 的实现类):
public interface Factory {
@NonNull
<T extends ViewModel > T create (@NonNull Class<T> modelClass) ;
}
只有一个 create 方法,用脚指头想想也知道肯定是用来初始化 ViewModel 的。继续看 of 方法:
return new ViewModelProvider (ViewModelStores.of(activity), sDefaultFactory)
出现了两个新的类 ViewModelProvider 与 ViewModelStores。
public class ViewModelProvider {
private static final String DEFAULT_KEY =
"android.arch.lifecycle.ViewModelProvider.DefaultKey" ;
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
……
@NonNull
@MainThread
public <T extends ViewModel > T get (@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
if (viewModel != null ) {
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
}
此类很简单,就是维护了一个 mFactory,一个新出现的类 ViewModelStore,并提供了用 mFactory 和 ViewModelStore 生成 ViewModel 的 get 方法。
再来看 ViewModelStores.of(activity):
public class ViewModelStores {
private ViewModelStores () {
}
@MainThread
public static ViewModelStore of (@NonNull FragmentActivity activity) {
return holderFragmentFor(activity).getViewModelStore();
}
@MainThread
public static ViewModelStore of (@NonNull Fragment fragment) {
return holderFragmentFor(fragment).getViewModelStore();
}
}
只有两个 of 方法,holderFragmentFor 为 HolderFragment 初始化的静态方法。
public class HolderFragment extends Fragment {
……
private ViewModelStore mViewModelStore = new ViewModelStore ();
public ViewModelStore getViewModelStore () {
return mViewModelStore;
}
……
}
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap <>();
final void put (String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.get(key);
if (oldViewModel != null ) {
oldViewModel.onCleared();
}
mMap.put(key, viewModel);
}
final ViewModel get (String key) {
return mMap.get(key);
}
public final void clear () {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
很明显是一个用来存放 ViewModel 实例的类,内部维护了一个 HashMap 存放 ViewModel,并提供了 get,put,clear 方法。
至此 ViewModelProviders.of 做了哪些事情呢:
初始化了 ViewModelProvider 内部维护了用于创建 VM 的 Factory,和用户存放 VM 的 ViewModelStore;
初始化了用来生成 ViewModel 的 Factory(默认为 DefaultFactory);
通过 ViewModelStores 的静态方法实例化了 HolderFragment,并实例化了 ViewModelStore。
然后是 ViewModelProvider 的 get 方法:
ViewModelProviders.of(activity, factory).get(MyViewModel.class);
@NonNull
@MainThread
public <T extends ViewModel > T get (@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
if (viewModel != null ) {
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
逻辑不复杂,先看 ViewModelStore 是不是已经存了,没有的话就通过 factory 实例化,并存到 ViewModelStore 中。
ViewModel 的死亡 public abstract class ViewModel {
@SuppressWarnings("WeakerAccess")
protected void onCleared () {
}
}
Vm 只有一个 onCleared 方法,那么他是在何时调用的呢?这里就要介绍之前提到的 HolderFragment 了:
public class HolderFragment extends Fragment {
private static final String LOG_TAG = "ViewModelStores" ;
private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager ();
private ViewModelStore mViewModelStore = new ViewModelStore ();
public HolderFragment () {
setRetainInstance(true );
}
@Override
public void onDestroy () {
super .onDestroy();
mViewModelStore.clear();
}
public ViewModelStore getViewModelStore () {
return mViewModelStore;
}
……
}
Vm 创建的时候提到过实例化了一个 HolderFragment。并且实例化的时候通过上面 createHolderFragment 方法将其 fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();。我们知道 commit 之后 fragment 将会拥有灵魂,获得生命周期。再看其 onDestroy 方法里调用了 mViewModelStore.clear():
public final void clear () {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
Google 充分利用了 fragment 生命周期的特性,使得 Vm 完成了 onCleared。Google 的 lifecycle 与 ViewModel 全都是利用 fragment 的一些特性来玩这些生命周期。
那么问题来了,为什么横竖屏切换 ViewModel 不会 onCleared?看 HolderFragment 的构造方法里有个 setRetainInstance(true);。
最佳实践与注意事项 由于 ViewModel 生命周期可能长于 Activity 生命周期,所以为了避免内存泄漏,Google 禁止在 ViewModel 中持有 Context 或 Activity 或 View 的引用。如果非得使用 Context,可以继承 AndroidViewModel 类中获取 ApplicationContext。
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel (@NonNull Application application) {
mApplication = application;
}
@NonNull
public <T extends Application > T getApplication () {
return (T) mApplication;
}
}
在现代 Android 开发中,推荐使用 Kotlin 协程结合 StateFlow 替代 LiveData,并使用 Hilt 等依赖注入框架自动管理 ViewModel 的生命周期,减少样板代码。同时,注意在 onCleared 中清理不必要的资源订阅,确保无内存泄漏。
总结来说,ViewModel 是 MVVM 架构中的核心组件,通过解耦 UI 控制器与业务数据,显著提升了代码的可测试性和可维护性。理解其底层实现有助于开发者更好地规避陷阱,构建健壮的应用程序。