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 | 存活范围 |
|---|---|
| Activity | Activity 生命周期(旋转存活,finish 销毁) |
| Fragment | Fragment 生命周期(同 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_KEY | Application 实例 |
SAVED_STATE_REGISTRY_OWNER_KEY | SavedStateRegistry 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.composehiltViewModel 是 viewModel() 的 Hilt 版本,@HiltViewModel 标注 + @Inject constructor 自动装配,@SavedStateHandle 参数自动注入——Meme 项目就是这个模式。
常见陷阱
- 不用
remember存 ViewModel:viewModel()自己管理实例,记住它反而破坏恢复。 - 不要在 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。