ViewModel:跨配置存活的 UI 状态 · AndroidX 源码指南
AAndroidX 源码指南
Lifecycle
应用基础 · androidx.lifecycle

ViewModel:跨配置存活的 UI 状态2.12.0-alpha01

作用域、Factory 与 CreationExtras、SavedStateHandle,以及 Compose 中的获取方式。

最后更新 2026-08-01

ViewModel 解决什么

class ProfileViewModel(
    private val repo: ProfileRepo,
) : ViewModel() {

    private val _profile = MutableStateFlow<ProfileState>(ProfileState.Loading)
    val profile: StateFlow<ProfileState> = _profile.asStateFlow()

    fun load() {
        viewModelScope.launch {
            _profile.value = ProfileState.Loading
            _profile.value = try {
                ProfileState.Loaded(repo.fetch())
            } catch (e: Exception) {
                ProfileState.Error(e)
            }
        }
    }
}
  • 配置变更(旋转、深色切换)后同一实例存活,数据不重载。
  • viewModelScope 随 ViewModel 清理自动取消协程。
  • 进程真正被杀死时 ViewModel 会消失——需要恢复用 SavedStateHandle。

作用域:谁来持有

Owner存活范围
ActivityActivity 生命周期(旋转存活,finish 销毁)
FragmentFragment 生命周期(同 Activity 内独立)
NavBackStackEntry该导航条目在 back stack 期间存活(返回栈弹出才销毁)

规则:ViewModel 属于”谁”由获取它的 owner 决定。Compose 里 viewModel()/hiltViewModel() 用最近的 LocalViewModelStoreOwner。Navigation3 中用 rememberViewModelStoreNavEntryDecorator() 让每个 NavEntry 有独立 owner。

Factory 与 CreationExtras:怎么传参数

class ProfileViewModel(
    private val repo: ProfileRepo,
    private val userId: String,
) : ViewModel()

val factory = viewModelFactory {
    initializer {
        ProfileViewModel(
            repo = app()[ProfileRepo::class],   // 从 extras 拿依赖
            userId = createSavedStateHandle().get<String>("userId") ?: "",
        )
    }
}

val vm: ProfileViewModel = viewModel(factory = factory)

initializer 作用域内可用的 extras key(源码 CreationExtras):

Key含义
APPLICATION_KEYApplication 实例
SAVED_STATE_REGISTRY_OWNER_KEYSavedStateRegistry owner(可建 SavedStateHandle)
VIEW_MODEL_STORE_OWNER_KEY当前 ViewModelStore owner
DEFAULT_ARGS_KEY默认构造参数(导航传参)

createSavedStateHandle() 是 initializer 作用域的扩展,等价于从 extras 取 registry 后构建 handle。

SavedStateHandle:进程恢复

class SearchViewModel(
    private val handle: SavedStateHandle,
) : ViewModel() {

    var query: String
        get() = handle["query"] ?: ""
        set(value) { handle["query"] = value }

    val results: StateFlow<List<Result>> =
        handle.getStateFlow("results", emptyList())
}
  • handle["key"] / handle["key"] = value 读写。
  • getStateFlow(key, initial) 得到可观察状态,进程重建时从 Bundle 恢复。
  • 注意容量限制:Bundle 序列化,不适合存大对象/大数据集。

Compose 中获取

// 普通 ViewModel
val vm: ProfileViewModel = viewModel(factory = factory)

// Hilt ViewModel(推荐:参数自动注入)
val vm: ProfileViewModel = hiltViewModel()  // androidx.hilt.lifecycle.viewmodel.compose

hiltViewModelviewModel() 的 Hilt 版本,@HiltViewModel 标注 + @Inject constructor 自动装配,@SavedStateHandle 参数自动注入——Meme 项目就是这个模式。

常见陷阱

  • 不用 remember 存 ViewModelviewModel() 自己管理实例,记住它反而破坏恢复。
  • 不要在 ViewModel 持有 UI 引用:Activity/Fragment/Compose 状态泄漏。
  • viewModelScope 只管自身:短任务用 lifecycleScope.repeatOnLifecycle,长任务用 WorkManager。
  • Factory 重复创建viewModel(factory) 每次重组调用是幂等的(store 按 key 缓存),但不要每次传新 factory 对象。
  • 进程重建 ≠ 配置变更:SavedStateHandle 是进程恢复的兜底,不是日常状态存放地。

复制即用

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewModelFactory
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow

class UserViewModel(
    private val repo: UserRepo,
    private val handle: SavedStateHandle,
) : ViewModel() {

    val userId: String
        get() = handle["userId"] ?: ""

    private val _state = MutableStateFlow<UserState>(UserState.Loading)
    val state = _state.asStateFlow()

    init { refresh() }

    fun refresh() {
        viewModelScope.launch {
            _state.value = UserState.Loading
            _state.value = try {
                UserState.Loaded(repo.fetch(userId))
            } catch (e: Exception) {
                UserState.Error(e)
            }
        }
    }
}

fun userViewModelFactory(repo: UserRepo, userId: String) = viewModelFactory {
    initializer {
        UserViewModel(
            repo = repo,
            handle = createSavedStateHandle().apply { this["userId"] = userId },
        )
    }
}

要点

  • ViewModel 跨配置变更存活;作用域由 owner(Activity/Fragment/NavEntry)决定。
  • 参数用 viewModelFactory + initializer + CreationExtras 注入;SavedStateHandle 管进程恢复。
  • Compose 用 viewModel() / hiltViewModel();Navigation3 需要 entry decorator 提供独立 owner。
  • 不要持有 UI 引用;短任务用 lifecycleScope,长任务用 WorkManager。

相关页面