目录
  1. 1. 一、ViewModel 的核心能力:跨配置重建
  2. 2. 二、源码解析:ViewModel 是如何”存活”下来的
    1. 2.1. 2.1 核心组件关系图
    2. 2.2. 2.2 NonConfigurationInstances 机制
    3. 2.3. 2.3 ComponentActivity 中的完整实现
    4. 2.4. 2.4 ViewModelStore 的实现
    5. 2.5. 2.5 Activity 销毁时 ViewModelStore 的清理时机
    6. 2.6. 2.6 Fragment 中的 ViewModel 存储
  3. 3. 三、ViewModelProvider 与 Factory
    1. 3.1. 3.1 ViewModelProvider 的核心流程
    2. 3.2. 3.2 自定义 Factory
    3. 3.3. 3.3 AndroidViewModel 与 Factory
    4. 3.4. 3.4 SavedStateHandle 的 Factory
  4. 4. 四、ViewModelScope:协程的最佳拍档
    1. 4.1. 4.1 viewModelScope 的实现
    2. 4.2. 4.2 使用示例
    3. 4.3. 4.3 SupervisorJob vs Job
  5. 5. 五、Fragment 间共享 ViewModel
    1. 5.1. 5.1 共享 ViewModel 的实现原理
    2. 5.2. 5.2 通过 Navigation Graph 作用域共享
  6. 6. 六、ViewModel 的生命周期与常见陷阱
    1. 6.1. 6.1 ViewModel 的完整生命周期
    2. 6.2. 6.2 常见陷阱
  7. 7. 七、ViewModel 与 SavedStateHandle 的协作
    1. 7.1. 7.1 双层保护机制
    2. 7.2. 7.2 实现示例
  8. 8. 面试常考问题
JetPack全家桶(三)之ViewModel界面控制器

一、ViewModel 的核心能力:跨配置重建

Android 的 Activity/Fragment 在屏幕旋转时会经历完整的销毁和重建流程,这会导致普通成员变量中的数据丢失。传统的解决方案是用 onSaveInstanceState() 将数据序列化保存再恢复,但这对大体积对象(如网络数据列表、Bitmap)效率极低且无法保存非序列化对象。ViewModel 的出现彻底解决了这个问题——ViewModel 存储于 ViewModelStore 中,而 ViewModelStore 的生命周期与 Activity 的配置变更无关。

但 ViewModel 的能力远不止这些。它还是 MVVM 架构的核心组件,承担着以下职责:

  1. 数据持有:持有 UI 所需的数据,生命周期长于 Activity/Fragment。
  2. 业务逻辑承载:承担数据获取、转换、缓存等业务逻辑,将 Activity/Fragment 解放为纯粹的 UI 控制器。
  3. Fragment 间通信:通过 Activity-scoped ViewModel 实现 Fragment 之间的数据共享。
  4. 协程集成:通过 viewModelScope 自动管理协程生命周期。

二、源码解析:ViewModel 是如何”存活”下来的

2.1 核心组件关系图

┌──────────────────────────┐
│ ViewModelStoreOwner │ (接口)
│ +getViewModelStore() │
└──────────┬───────────────┘
│ implements
┌──────┴──────────────────────┐
│ ComponentActivity │
│ Fragment │
└─────────────────────────────┘
│ 持有
┌──────▼──────────────┐
│ ViewModelStore │ (HashMap<String, ViewModel>)
│ +put(key, vm) │
│ +get(key): ViewModel │
│ +clear() │ ← 在 onDestroy (非配置变更) 时调用
└──────┬──────────────┘
│ 管理
┌──────▼──────────────┐
│ ViewModel │ (抽象类)
│ +onCleared() │ ← ViewModel 被清除时调用
│ +viewModelScope │ ← Kotlin 扩展,自动取消协程
└─────────────────────┘

┌──────┴──────────────┐
│ AndroidViewModel │ (持有 Application Context)
│ -mApplication │
└─────────────────────┘

2.2 NonConfigurationInstances 机制

核心在于 ViewModelStoreOwnerViewModelStore。以 Activity 为例(AOSP 路径:/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.kt),ComponentActivity 实现了 ViewModelStoreOwner 接口。Activity 首次创建时会 new ViewModelStore(),在配置变更重建时,系统通过 NonConfigurationInstances 机制将旧的 ViewModelStore 传递给新的 Activity 实例。

关键代码路径:

// ComponentActivity.java (androidx.activity)
getLastNonConfigurationInstance() -> NonConfigurationInstances.viewModelStore

当 Activity 因屏幕旋转而销毁时,系统在 retainNonConfigurationInstances() 中将 ViewModelStore 保存;新建 Activity 时通过 getLastNonConfigurationInstance() 恢复。ViewModelStore 内部是一个 HashMap<String, ViewModel>,key 来自 ViewModel 类名,确保同名 ViewModel 获取的是同一个实例。

2.3 ComponentActivity 中的完整实现

// ComponentActivity.java (androidx.activity)
public class ComponentActivity extends Activity
implements ViewModelStoreOwner, LifecycleOwner, ... {

// ViewModelStore 的存储位置
private ViewModelStore mViewModelStore;

@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException(
"Your activity is not yet attached to the Application instance.");
}
if (mViewModelStore == null) {
// 1. 尝试从 NonConfigurationInstances 获取上次保留的 ViewModelStore
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
// 2. 如果没有(首次创建),新建一个
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}

// 当 Activity 因配置变更(如屏幕旋转)即将销毁时调用
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
// ...
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.viewModelStore = mViewModelStore; // 保留 ViewModelStore
// ...
return nci;
}
}

2.4 ViewModelStore 的实现

// ViewModelStore.java
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(); // 旧 ViewModel 被替换时清理
}
}

final ViewModel get(String key) {
return mMap.get(key);
}

// 清除所有 ViewModel
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear(); // 调用每个 ViewModel 的清理逻辑
}
mMap.clear();
}
}

2.5 Activity 销毁时 ViewModelStore 的清理时机

ViewModelStore 什么时候被清理?关键区别在于配置变更真正的销毁

// ComponentActivity.java
@Override
protected void onDestroy() {
super.onDestroy();
// isChangingConfigurations() 判断是否为配置变更引起的销毁
if (mViewModelStore != null && !isChangingConfigurations()) {
// 真正的销毁(用户按 Back、finish()、系统回收):清除 ViewModelStore
mViewModelStore.clear();
}
// 如果是配置变更:不清除,ViewModelStore 通过 onRetainNonConfigurationInstance 保留
}

这就是 ViewModel 能跨配置重建存活的核心逻辑——配置变更时 ViewModelStore 不执行 clear(),而是被 onRetainNonConfigurationInstance() 保留并传递给新 Activity。

2.6 Fragment 中的 ViewModel 存储

Fragment 的 ViewModelStore 存储在 FragmentManager 中:

// Fragment.java
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
// ViewModelStore 实际存储在 FragmentManagerViewModel 中
return mFragmentManager.getViewModelStore(this);
}

// FragmentManagerViewModel.java (内部类)
// 这是一个 ViewModel 本身——它存储在 Fragment 所在的宿主 Activity 的 ViewModelStore 中
// 其内部维护了 HashMap<String, ViewModelStore> 来管理每个 Fragment 的 ViewModelStore

这个设计非常精妙——FragmentManager 的所有 Fragment 的 ViewModelStore 都被存储在一个特殊的 FragmentManagerViewModel 中,而这个 ViewModel 又存储在 Activity 的 ViewModelStore 中。因此:

  • Activity 配置变更:Activity 的 ViewModelStore 被保留 -> FragmentManagerViewModel 被保留 -> 所有 Fragment 的 ViewModelStore 被保留
  • Activity 真正销毁:Activity 的 ViewModelStore.clear() -> FragmentManagerViewModel.onCleared() -> 所有 Fragment 的 ViewModelStore.clear()

三、ViewModelProvider 与 Factory

ViewModelProvider 是获取 ViewModel 的门面类,通过 ViewModelStoreOwner 拿到 ViewModelStore。默认的 Factory 通过反射调用 ViewModel 的无参构造。如果 ViewModel 需要构造参数(如注入 Repository),需要使用自定义 Factory。

3.1 ViewModelProvider 的核心流程

// ViewModelProvider.kt
class ViewModelProvider(
private val store: ViewModelStore,
private val factory: Factory
) {
@MainThread
fun <T : ViewModel> get(modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName
?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}

@MainThread
fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
// 1. 先尝试从 ViewModelStore 中获取已存在的 ViewModel
val viewModel = store.get(key)
if (modelClass.isInstance(viewModel)) {
// ViewModel 已在配置变更后恢复,直接返回
return modelClass.cast(viewModel)
}

// 2. 使用 Factory 创建新的 ViewModel 实例
val newViewModel: ViewModel = factory.create(modelClass)

// 3. 存入 ViewModelStore(如果已存在则替换旧的)
store.put(key, newViewModel)

return newViewModel
}

interface Factory {
fun <T : ViewModel> create(modelClass: Class<T>): T
}
}

// 默认 Factory 通过反射调用无参构造函数
class NewInstanceFactory : Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return modelClass.getDeclaredConstructor().newInstance()
}
}

3.2 自定义 Factory

当 ViewModel 需要构造参数(如注入 Repository)时,需要使用自定义 Factory:

class MyViewModel(private val repo: Repository) : ViewModel()

class MyFactory(private val repo: Repository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel(repo) as T
}
}

// 使用
val repo = Repository()
val viewModel = ViewModelProvider(this, MyFactory(repo))
.get(MyViewModel::class.java)

3.3 AndroidViewModel 与 Factory

AndroidViewModel 是 ViewModel 的子类,在构造函数中接收 Application 实例:

// 使用 AndroidViewModel 的 Application Context
class MyAndroidViewModel(application: Application) : AndroidViewModel(application) {
private val sharedPrefs = application.getSharedPreferences("my_prefs", Context.MODE_PRIVATE)

fun saveValue(key: String, value: String) {
sharedPrefs.edit().putString(key, value).apply()
}

fun getValue(key: String): String? {
return sharedPrefs.getString(key, null)
}
}

// 不需要自定义 Factory,使用系统提供的 AndroidViewModelFactory
val viewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory(application))
.get(MyAndroidViewModel::class.java)

3.4 SavedStateHandle 的 Factory

从 lifecycle-viewmodel-savedstate 开始,Google 推荐使用 SavedStateHandle 来保存进程重建后的状态:

class MySavedStateViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {

val query: LiveData<String> = savedStateHandle.getLiveData("search_query", "")
val filter: LiveData<Int> = savedStateHandle.getLiveData("active_filter", 0)

fun updateQuery(newQuery: String) {
savedStateHandle["search_query"] = newQuery
}

fun updateFilter(newFilter: Int) {
savedStateHandle["active_filter"] = newFilter
}
}

// 使用默认的 SavedStateViewModelFactory
// 这个 Factory 由 ComponentActivity 默认提供
class MainActivity : AppCompatActivity() {
private val viewModel: MySavedStateViewModel by viewModels()
// viewModels() 委托默认使用 SavedStateViewModelFactory
}

SavedStateHandle 的底层原理:

  1. 它本质是一个 Bundle 的包装器。
  2. 在 Activity 的 onSaveInstanceState() 被调用时,SavedStateHandle 中的数据被序列化到 Bundle 中。
  3. 进程重建后,数据从 Bundle 恢复。
  4. 这与 ViewModel 的 NonConfigurationInstances 机制互补——ViewModelStore 负责跨配置变更存活,SavedStateHandle 负责跨进程死亡恢复。

四、ViewModelScope:协程的最佳拍档

ViewModel 实现的另一个重要扩展是 viewModelScope(来自 lifecycle-viewmodel-ktx)。它是一个绑定到 ViewModel 的 CoroutineScope,在 ViewModel 的 onCleared() 方法被调用时自动取消。这意味着启动在 viewModelScope 中的所有协程无需手动管理生命周期,彻底解决了”协程泄露导致内存泄漏”的问题。

4.1 viewModelScope 的实现

// lifecycle-viewmodel-ktx
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
}

internal class CloseableCoroutineScope(
context: CoroutineContext
) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context

override fun close() {
coroutineContext.cancel()
}
}

当 ViewModel 的 clear() 被调用时:

// ViewModel.java
final void clear() {
mCleared = true;
// ...
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
closeWithRuntimeException(value); // 关闭 viewModelScope (Closeable)
}
}
}
onCleared();
}

4.2 使用示例

class UserViewModel : ViewModel() {
private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> = _users

init {
viewModelScope.launch(Dispatchers.IO) {
_users.postValue(userRepository.getUsers())
}
}

fun refresh() {
viewModelScope.launch {
try {
_users.value = userRepository.getUsers()
} catch (e: Exception) {
// 处理错误
}
// 如果 ViewModel 在协程完成前被清除了,协程会被自动取消
// 不会更新 UI,也不会泄漏
}
}
}

4.3 SupervisorJob vs Job

viewModelScope 使用 SupervisorJob() 作为协程的父 Job。这意味着:

  • SupervisorJob:子协程的失败不会影响其他兄弟协程。如果一个 launch 中的协程抛出异常被捕获,其他协程继续运行。
  • 普通 Job:一个子协程失败,所有兄弟协程也会被取消。

这对 UI 场景非常重要——你不想因为一个数据加载失败而导致所有并行的请求都被取消。

五、Fragment 间共享 ViewModel

Fragment 之间共享 ViewModel 是 MVVM 架构中常见的需求。Android 推荐使用 Activity-scoped ViewModel 而非 Fragment 间接口回调:

// 在 Fragment 中使用 Activity-scoped ViewModel
class MasterFragment : Fragment() {
// 使用 activityViewModels() 委托,获取绑定到 Activity 的 ViewModel
private val sharedViewModel: SharedViewModel by activityViewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// 点击列表项
listView.setOnItemClickListener { _, _, position, _ ->
sharedViewModel.selectItem(position)
}
}
}

class DetailFragment : Fragment() {
private val sharedViewModel: SharedViewModel by activityViewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// 观察选中项的变化
sharedViewModel.selectedItem.observe(viewLifecycleOwner) { item ->
// 更新详情 UI
updateDetail(item)
}
}
}

class SharedViewModel : ViewModel() {
private val _selectedItem = MutableLiveData<Item>()
val selectedItem: LiveData<Item> = _selectedItem

fun selectItem(position: Int) {
_selectedItem.value = items[position]
}
}

5.1 共享 ViewModel 的实现原理

activityViewModels() 委托的实现:

// FragmentViewModelLazy.kt
@MainThread
inline fun <reified VM : ViewModel> Fragment.activityViewModels(
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
requireActivity().defaultViewModelProviderFactory
}
return ViewModelLazy(VM::class, {
requireActivity().viewModelStore
}, factoryPromise)
}

关键在于 requireActivity().viewModelStore——使用的是 Activity 级别的 ViewModelStore 而非 Fragment 自己的。因此所有 Fragment 拿到的都是同一个 ViewModel 实例。

5.2 通过 Navigation Graph 作用域共享

Android Navigation 组件也支持 ViewModel 共享:

// 在 Navigation 中,ViewModel 可以绑定到 NavBackStackEntry
class DetailFragment : Fragment() {
// 获取绑定到当前 destination 的 navBackStackEntry 的 ViewModel
private val navViewModel: MyViewModel by navGraphViewModels(R.id.my_nav_graph)
}

navGraphViewModels 会在 NavGraph 的范围内保持 ViewModel,当该 NavGraph 被弹出栈时,ViewModel 的 onCleared() 才会被调用。

六、ViewModel 的生命周期与常见陷阱

6.1 ViewModel 的完整生命周期

Activity/Fragment 首次创建


ViewModelProvider.get() 首次调用


Factory.create() -> 创建 ViewModel 实例


ViewModel 存入 ViewModelStore

│ 配置变更(如屏幕旋转)
│ │
│ ▼
│ onRetainNonConfigurationInstance()
│ │ ViewModelStore 被保留
│ ▼
│ 新 Activity 通过 getLastNonConfigurationInstance()
│ 取回 ViewModelStore
│ │
│ ▼
│ ViewModelProvider.get() 返回同一个 ViewModel

│ 真正的销毁 (finish() / back / process death)
│ │
│ ▼
│ onDestroy() && !isChangingConfigurations()
│ │
│ ▼
│ ViewModelStore.clear()
│ │
│ ▼
│ 对每个 ViewModel:close viewModelScope tags -> onCleared()
│ ▼
│ ViewModel 释放所有资源

6.2 常见陷阱

陷阱一:在 ViewModel 中持有 Activity/View/Context 引用

class BadViewModel : ViewModel() {
// 严重错误!Activity 在配置变更后被销毁重建,
// 但 ViewModel 存活下来,持有旧 Activity 引用导致内存泄漏
private val context: Context = getSomeActivityContext()

// 也不应该持有 View 引用
// 不要将任何 UI 组件(View、Adapter、Drawable 等)传入 ViewModel
}

// 正确做法:如果需要 Context,使用 AndroidViewModel 的 Application Context
class GoodViewModel(application: Application) : AndroidViewModel(application) {
private val appContext = application.applicationContext // 生命周期 == 应用进程

fun getFileDir(): File = appContext.filesDir // 安全使用
}

陷阱二:过度使用 ViewModel

ViewModel 不是万能的。以下情况不应该使用 ViewModel:

  • 简单的 Fragment 配置状态(用 onSaveInstanceState 更合适)
  • 跨 Activity 共享数据(应该用 Repository 单例 + 持久化)
  • 跨进程数据传递(应该用 ContentProvider 或文件)
  • 一次性事件(如导航指令、Snackbar——这些应该用 SingleLiveEventChannel

陷阱三:ViewModel 中发射数据到 UI 的时机

class MyViewModel : ViewModel() {
val data = MutableLiveData<String>()

init {
// ViewModel 创建时就开始加载数据
viewModelScope.launch {
data.value = loadData()
}
}

override fun onCleared() {
super.onCleared()
// 清理资源:关闭数据库连接、取消网络请求、注销监听器等
}
}

// Activity 中
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// 关键:即使你在这里才 observe,LiveData 会立即把最后的值发给你
// (LiveData 是粘性的 sticky)
viewModel.data.observe(this) { data ->
// 如果是配置变更后重建,你可能已经在加载完数据之后才 observe
// LiveData 仍会把缓存的数据发送给你
updateUI(data)
}
}
}

七、ViewModel 与 SavedStateHandle 的协作

7.1 双层保护机制

                  配置变更(旋转屏幕)
──────────────────► ViewModelStore 存活
│ (通过 NonConfigurationInstances)

进程死亡(低内存/用户杀进程)
──────────────────────────────► SavedStateHandle 保存到 Bundle
(通过 onSaveInstanceState)

进程恢复时从 Bundle 恢复

这是一个设计精巧的”双层保险”:

  • 配置变更 → ViewModel 在内存中保持,零开销
  • 进程死亡 → SavedStateHandle 将关键状态序列化,恢复后自动填充

7.2 实现示例

class SearchViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {

companion object {
private const val KEY_QUERY = "search_query"
private const val KEY_PAGE = "current_page"
}

// 自动绑定到 SavedStateHandle
val query: LiveData<String> = savedStateHandle.getLiveData(KEY_QUERY, "")
val currentPage: LiveData<Int> = savedStateHandle.getLiveData(KEY_PAGE, 1)

private val _results = MutableLiveData<List<Result>>()
val results: LiveData<List<Result>> = _results

init {
// 如果有保存的状态(进程重建后),自动从这个初始值开始
search(query.value ?: "")
}

fun search(newQuery: String) {
savedStateHandle[KEY_QUERY] = newQuery
savedStateHandle[KEY_PAGE] = 1

viewModelScope.launch {
_results.value = repository.search(newQuery, 1)
}
}

fun loadNextPage() {
val nextPage = (currentPage.value ?: 1) + 1
savedStateHandle[KEY_PAGE] = nextPage

viewModelScope.launch {
val moreResults = repository.search(query.value ?: "", nextPage)
_results.value = (_results.value ?: emptyList()) + moreResults
}
}
}

面试常考问题

Q1: ViewModel 与 onSaveInstanceState 如何选择?

A: 这是 Android 面试中的经典问题,考察候选人对组件边界的理解。

ViewModel(通过 NonConfigurationInstances):

  • 存活场景:仅配置变更(屏幕旋转、语言切换、键盘可用性变化等)
  • 存储位置:内存(ViewModelStore 由 Activity 实例持有,通过 ActivityClientRecord 传递)
  • 容量:无限制(受设备 RAM 限制)
  • 数据类型:任何 Java/Kotlin 对象
  • 适用场景:UI 数据列表、网络请求结果、Bitmap、Repository 实例
  • 性能:零开销(纯内存操作,无序列化/反序列化)

onSaveInstanceState(通过 Bundle):

  • 存活场景:配置变更 + 进程死亡(系统杀进程 + 用户返回)
  • 存储位置:系统进程内存 + 磁盘(ActivityManagerService 持有)
  • 容量:约 1MB 限制(超过会抛出 TransactionTooLargeException)
  • 数据类型:可序列化/可 Parcelable 的简单数据
  • 适用场景:UI 状态标记(输入框文本、列表滚动位置、Tab 选中索引)
  • 性能:有序列化开销

实际项目中的最佳实践是两种机制配合使用

  • ViewModel 持有业务数据和复杂对象
  • SavedStateHandle(ViewModel 内)或 onSaveInstanceState 保存关键 UI 状态
  • Google 推荐使用 SavedStateHandle 作为 ViewModel 的构造参数,这是因为它将两种机制统一了起来

Q2: Fragment 之间如何共享 ViewModel?

A: 使用 Activity 级别的 ViewModelStoreOwner。在 Fragment 中通过 ViewModelProvider(requireActivity()) 获取 ViewModel,两个 Fragment 拿到的将是同一个 ViewModel 实例。这种方式比传统接口回调或 EventBus 更简洁且无损(不依赖序列化/AIDL),是 Google 推荐的 Fragment 间通信方案。

实现原理:

  1. 每个 Fragment 有自己的 ViewModelStore(存储在 FragmentManagerViewModel 中)
  2. Activity 有自己的 ViewModelStore(存储在 ComponentActivity 中)
  3. 通过 requireActivity().viewModelStore 获取 Activity 的 ViewModelStore
  4. 所有使用此 ViewModelStore 的 Fragment 共享同一 ViewModel 实例

Kotlin 中的便捷写法:

  • by viewModels() — Fragment 作用域(自己的 ViewModelStore)
  • by activityViewModels() — Activity 作用域(Activity 的 ViewModelStore)
  • by navGraphViewModels(R.id.nav_graph) — NavGraph 作用域

Q3: ViewModel 能持有 Context 的引用吗?

A: 不能持有 Activity/Fragment 的 Context。AndroidViewModel 是 ViewModel 的子类,其构造函数接受 Application Context 引用,这个 Context 生命周期与应用进程相同,不存在泄漏风险。如果 ViewModel 需要 Context 做临时操作(如查询数据库路径),应该使用依赖注入传入 Application Context,而非 Activity 的 Context。

为什么 Activity Context 是危险的:

  • ViewModel 的生命周期可能比 Activity 长(屏幕旋转后 Activity 被销毁,ViewModel 存活)
  • 持有旧 Activity Context 会导致内存泄漏(GC 无法回收被 ViewModel 引用的 Activity)
  • 更可怕的是,使用这个 Context 启动的 Dialog/Toast 会引用已销毁的 Activity 的 Window,导致 WindowLeaked 异常

安全的 Context 使用方式:

// 安全:Application Context
class MyViewModel(application: Application) : AndroidViewModel(application) {
fun getPath(): File {
return application.filesDir // OK - 生命周期 == 进程
}
}

// 危险:Activity Context(通过构造函数注入)
class BadViewModel(private val activityContext: Context) : ViewModel() {
// 屏幕旋转后,activityContext 是旧 Activity 的引用
}

// 安全替代方案:通过 SavedStateHandle 传递 Context 相关的结果
class GoodViewModel(
private val app: Application,
private val savedStateHandle: SavedStateHandle
) : AndroidViewModel(app) {
fun getResourceUri(): Uri {
return Uri.fromFile(File(app.filesDir, "data.json"))
}
}

Q4: ViewModel 的 onCleared() 何时被调用?

A: onCleared() 在 ViewModel 不再被使用且将被销毁时调用。具体触发时机:

  1. Activity 真正销毁(finish()、按 Back 键退出的 onDestroy),且不是配置变更(!isChangingConfigurations())时,ViewModelStore.clear() -> 对每个 ViewModel 调用 clear() -> onCleared()
  2. Fragment 在以下情况:
    • Fragment 从 back stack 中永久性移除(pop + 不添加到 back stack)
    • Fragment 所在 Activity 被销毁(且不是配置变更)
  3. NavGraph 作用域:当对应的 NavGraph 被完全弹出 back stack 时。

注意:onCleared() 不会在配置变更时被调用,因为此时 ViewModel 仍然存活。
注意:进程被系统杀死时,onCleared() 也不会被调用(进程直接被 SIGKILL 终止)。因此不能依赖 onCleared() 来保存关键数据——应该使用 SavedStateHandle

Q5: ViewModelProvider.Factory 的几种常用实现方式的区别?

A: ViewModel 的创建主要通过 Factory 模式。根据 ViewModel 的构造参数不同,有以下几种常用的 Factory:

  1. 无参构造函数 + 默认 Factory

    class SimpleViewModel : ViewModel()  // 无参构造
    // ViewModelProvider(this).get(SimpleViewModel::class.java)
    // 内部使用 NewInstanceFactory,通过反射调用无参构造函数
  2. AndroidViewModel + AndroidViewModelFactory

    class MyVM(application: Application) : AndroidViewModel(application)
    // 使用 ViewModelProvider.AndroidViewModelFactory.getInstance(application)
    // 通过反射调用单参数(Application)构造函数
  3. SavedStateHandle + SavedStateViewModelFactory

    class MyVM(private val handle: SavedStateHandle) : ViewModel()
    // ComponentActivity 默认的 Factory 同时支持 SavedStateHandle 和 Application 参数
    // 它是 SavedStateViewModelFactory,内部封装了 SavedStateRegistry
  4. 带自定义参数的 ViewModel + 自定义 Factory

    class MyVM(private val repo: Repository, private val userId: String) : ViewModel()
    class MyFactory(private val repo: Repository, private val userId: String) :
    ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
    return MyVM(repo, userId) as T
    }
    }
  5. Hilt/Dagger 注入 + @HiltViewModel(推荐):

    @HiltViewModel
    class MyVM @Inject constructor(
    private val repo: Repository,
    private val savedStateHandle: SavedStateHandle
    ) : ViewModel()
    // Hilt 在编译期生成 Factory,自动处理所有依赖注入
    // 这是 Google 当前推荐的方式

核心参考 AOSP 路径:

  • lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.kt — ViewModel 获取入口
  • lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelStore.java — ViewModel 存储
  • lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModel.java — ViewModel 基类
  • activity/activity/src/main/java/androidx/activity/ComponentActivity.java — NonConfigurationInstances 机制
  • fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java — Fragment ViewModelStore
  • lifecycle/viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateViewModelFactory.java — SavedState 集成
  • lifecycle/lifecycle-viewmodel-ktx/src/main/java/androidx/lifecycle/ViewModel.kt — viewModelScope 扩展
打赏
  • 微信
  • 支付宝

评论