一文读懂 ViewModel

一、核心作用(解决的核心问题)

ViewModel 是 Android 架构组件的核心,核心目标是分离 UI 控制器(Activity/Fragment)与数据逻辑,并解决「配置变更(如横竖屏切换)时数据丢失」的问题。

二、为什么需要 ViewModel?(onSaveInstanceState 的局限性)

以往通过 onSaveInstanceState + Bundle 保存状态存在明显缺陷(仅适用于轻量序列化数据),核心原因:

  1. 设计定位问题onSaveInstanceState 仅用于临时状态恢复(如旋转、内存不足回收),非持久化;且运行在主线程,若允许存储大量数据会导致 Activity 销毁耗时。
  2. 数据类型限制:Bundle 底层仅支持有限的可序列化类型,无法存储复杂数据。
  3. 性能开销:序列化 / 反序列化过程有额外性能损耗(Bundle 是跨进程通信载体,Activity 状态需通过 Binder 传递给系统进程暂存)。

三、ViewModel 核心能力

  1. 配置变更时保留状态:常见配置变更(横竖屏切换、分辨率调整、权限变更、系统字体样式变更)后,ViewModel 仍持有原有数据,无需重新加载。
  2. MVVM 架构解耦核心:ViewModel 不持有 View/Activity 引用,仅暴露数据和业务方法,实现 View 与 ViewModel 完全解耦。
  3. 生命周期感知:感知 UI Controller(Activity/Fragment)的生命周期,UI 重建后仍能获取同一个 ViewModel 实例;UI 真正销毁时自动释放资源。

四、ViewModel 具体使用

1. 基础使用(无参构造)

步骤 1:定义 ViewModel 类(继承 ViewModel)

scala 复制代码
// MyViewModel.java
public class MyViewModel extends ViewModel {
   // 存储需要跨配置变更保留的数据
   public int myInt = 0;
}

步骤 2:UI Controller 中绑定 ViewModel

scss 复制代码
// MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   // 绑定ViewModel到当前Activity(核心:ViewModelProvider(this))
   MyViewModel mViewModel = new ViewModelProvider(this).get(MyViewModel.class);
    // 后续可直接操作mViewModel中的数据
}

2. 进阶使用(带参构造:自定义 Factory)

ViewModelProviders 默认使用无参构造,若需传入参数(如数据库实例),需实现 ViewModelProvider.Factory 接口:

步骤 1:实现 Factory 类

java 复制代码
// MyViewModelFactory.java
public class MyViewModelFactory implements ViewModelProvider.Factory {
   // 需传入的依赖(如数据库、仓库等)
   private AppDatabase mAppDatabase;

   // 构造函数接收依赖
   public MyViewModelFactory(AppDatabase appDatabase) {
       mAppDatabase = appDatabase;
   }

   // 重写create方法,创建带参ViewModel实例
   @Override
   public <T extends ViewModel> T create(Class<T> modelClass) {
       return (T) new MyViewModel(mAppDatabase);
   }
}

步骤 2:定义带参 ViewModel

scala 复制代码
public class MyViewModel extends ViewModel {
   // 带参构造
   public MyViewModel(AppDatabase appDatabase) {
       // 初始化业务逻辑(如初始化仓库、数据请求等)
   }
}

步骤 3:使用 Factory 创建 ViewModel

scss 复制代码
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 1. 创建Factory实例,传入依赖
    MyViewModelFactory factory = new MyViewModelFactory(database);
    // 2. 通过Factory创建带参ViewModel
    MyViewModel viewModel = new ViewModelProvider(this, factory).get(MyViewModel.class);
}

3. 扩展场景:Fragment 间数据共享

通过传入 getActivity() 让多个 Fragment 共享同一个 ViewModel 实例,实现 Fragment 间通信:

csharp 复制代码
// Fragment 中获取与宿主Activity同生命周期的ViewModel
// Activity scope --- 多个Fragment共享,用于跨Fragment通信
FragmentCommunicationViewModel mCommunicationViewModel = 
    new ViewModelProvider(getActivity()).get(FragmentCommunicationViewModel.class);

五、ViewModel 生命周期 & 注意事项

1. 存活时间(取决于绑定的 ViewModelStoreOwner)

表格

绑定对象 存活周期 适用场景
Activity(this) 存活至 Activity finish()(配置变更的 onDestroy 不销毁) Activity 内数据跨配置变更保留
Fragment(this) 存活至 Fragment 被移除 Fragment 自身私有数据
Fragment(getActivity()) 与宿主 Activity 同生命周期 多 Fragment 间数据共享

2. 唯一生命周期回调:onCleared ()

  • onCleared() 是 ViewModel 唯一的生命周期回调方法;
  • 触发时机:ViewModel 绑定的 Owner(Activity/Fragment)真正销毁时(如 Activity finish ()、Fragment 被移除);
  • 用途:释放资源(如取消网络请求、关闭数据库连接),避免内存泄漏。

3. 核心注意事项(必看)

禁止在 ViewModel 中持有 UI Controller(Activity/Fragment)或 Context :ViewModel 寿命长于 UI Controller,持有引用会导致 UI 组件无法被 GC 回收,引发内存泄漏。✅ 若需 Context,可使用 AndroidViewModel(继承 ViewModel,持有 Application 级 Context,生命周期与应用一致)。

核心使用示例总结

csharp 复制代码
// 1. Activity 私有(同Activity生命周期)
new ViewModelProvider(this).get(MmsTabViewModel.class);

// 2. 多Fragment共享(宿主Activity生命周期)
new ViewModelProvider(getActivity()).get(FragmentCommunicationViewModel.class);

// 3. Fragment 私有(同Fragment生命周期)
new ViewModelProvider(this).get(ConversationBaseViewModel.class);

总结

  1. ViewModel 核心解决「配置变更数据丢失」和「UI 与数据逻辑解耦」问题,替代 onSaveInstanceState 处理非轻量数据;
  2. ViewModel 存活周期由绑定的 ViewModelStoreOwner 决定,onCleared() 是唯一资源释放入口;
  3. 严禁持有 UI/Context 引用,带参构造需通过自定义 Factory 实现,多 Fragment 通信可绑定 Activity 级 ViewModel。
相关推荐
程序员JerrySUN2 小时前
别再把 HTTPS 和 OTA 看成两回事:一篇讲透 HTTPS 协议、安全通信机制与 Mender 升级加密链路的完整文章
android·java·开发语言·深度学习·流程图
音视频牛哥2 小时前
Android平台GB28181设备接入模块架构解析、功能详解与典型应用场景分析
android·android gb28181·gb28181安卓端·gb28181对接·gb28181设备·gb28181语音广播·安卓gb28181设备对接
叁两2 小时前
前端开发如何快速上手安卓APP开发?
android
guodashen0072 小时前
在安卓端启动一个服务器接口,用于接收post请求的json数据
android·服务器·json
hindon3 小时前
一文读懂Android 中的 MVC、MVP、MVVM
android
漏刻有时3 小时前
CentOS 不定时 OOM 根治方案:PHP-FPM 进程管控 + Swap 扩容 + 全维度监控
android·centos·php
恋猫de小郭4 小时前
Android 性能迎来提升:内核引入 AutoFDO 普惠所有 15-16 设备
android·前端·flutter
CS_Zero5 小时前
Android ADB调试工具使用简记
android·adb
牢七5 小时前
Slim-4.x php审计 报错分析
android·开发语言·ide·安全·php