最近 AndroidX 给「跨组件 Compose 状态托管场景」引入了 AppState ,目前还没完全落地,但是从代码骨架可以看出来,AppState 的核心目的是:
把 Compose 的可观察状态从某个 「Composable/组件生命周期」里提出来,然后放到更上层的状态容器里管理。
这次新增的是 androidx.appstate 包,对应的核心目前有三个公开类型:
r
AppState
AppStateKey<T>
AppStateToken
而对应的公开方法包括:
kotlin
class AppState {
fun <T> getState(stateKey: AppStateKey<T>, defaultValue: T): State<T>
fun <T> setState(stateKey: AppStateKey<T>, value: T)
fun <T> updateState(stateKey: AppStateKey<T>, defaultValue: T, update: (T) -> T)
fun addAppStateListener(...): AppStateToken
fun removeAppStateListener(token: AppStateToken)
}
另外提交里也说了:Added new library to help support hoisting of Compose state ,也就是 AppState 不是用来「替代 ViewModel 」,也不是说「提供完整架构层状态管理 」,而是 help support hoisting of Compose state。
那为什么需要 AppState?首先我们知道,Compose 原本的状态模型大概是这样的流程:

因为 Compose 是声明式 UI,而 UI 是状态的表现,所以 MutableState.value 发声改变,会触发读取它的 composable 重组。
但问题在于状态放在哪里:

remember 存在 Composition ,而如果调用它的 composable 被移除后会丢失,而 rememberSaveable 主要是帮助状态跨重组、Activity recreation、系统发起的进程恢复等场景。
所以针对这个场景,AppState 的注释也是说了:
vbnet
Class that contains a store that maintains compose state beyond individual Android components.
There is no limitation on where these instance should be instantiated as that is left up to the developer.
也就是它不是把状态绑定到某个 Activity、Fragment、NavBackStackEntry 或者单个 Composable,而是让你自己决定 AppState 实例的作用域,换句话说:
Compose runtime 的
State<T>/MutableState<T>还是是底层可观察状态模型,但状态容器不再一定活在某个 Composable 里,而是可以被提升到更外层。
所以实际上 AppState 就是一个 keyed MutableState store ,这次源码里最核心的就是这两个 map:
swift
private val stateStore: MutableMap<AppStateKey<*>, MutableState<*>> = mutableMapOf()
private val appStateListeners = mutableMapOf<AppStateToken, CoroutineScope>()
第一个 stateStore 是真正存状态的地方,第二个 appStateListeners 是给未来监听器准备的 token 到 coroutine scope 映射:

实际上这里的重要点是:
AppState存的不是普通值,而是MutableState<*>,也就是说它不是一个传统Map<Key, Value>,而是一个 Compose runtime 可观察状态仓库
- 外部
getState()返回的是State<T>,不是MutableState<T>:
kotlin
public fun <T> getState(stateKey: AppStateKey<T>, defaultValue: T): State<T> {
@Suppress("UNCHECKED_CAST")
return stateStore.getOrPut(stateKey) { mutableStateOf(defaultValue) } as MutableState<T>
}
这意味着在 API 设计上倾向于:

也就是读写分离: 读的时候拿 State,写的时候走 AppState 的 set/update API。
而对于 getState 用的是 lazy 创建 State ,getState() 的逻辑很简单:
scss
stateStore.getOrPut(stateKey) { mutableStateOf(defaultValue) }

也就是它不是每次都拿值,而是拿同一个
MutableState对象,只要AppState实例还活着,对应 key 的 state 就还在。
从这里也能看出它和普通 remember { mutableStateOf() } 的不同:
remember的生命周期跟 Composition 位置绑定,而 AppState 的生命周期跟 AppState 实例本身绑定。
而 setState 第一次设置时可能注册会 auto-clear listener,其实 setState() 也是这个库目前最有意思的地方:
less
public fun <T> setState(stateKey: AppStateKey<T>, value: T) {
if (!stateStore.contains(stateKey) && stateKey.autoClearKey != null) {
addAppStateListener { map ->
val key = stateKey.autoClearKey
val currentValue = map[key]?.value
val initialValue = remember { currentValue }
if (currentValue != initialValue && stateKey.predicate(this@AppState)) {
LaunchedEffect(currentValue) {
stateStore.remove(stateKey)
removeAppStateListener(this@addAppStateListener)
}
}
}
}
(getState(stateKey, value) as MutableState<T>).value = value
}

也就是
AppStateKey不只是一个普通 key,它还能定义「什么时候这个状态应该被自动清理」。
另外,对应的 updateState 方法看起来是典型 reducer 风格:
kotlin
public fun <T> updateState(
stateKey: AppStateKey<T>,
defaultValue: T,
update: (T) -> T
) {
val currentState = getState(stateKey, defaultValue)
setState(stateKey, update(currentState.value))
}

这个 API 设计很像 reducer :
javascript
appState.updateState(IntKey, 0) { it + 1 }
按照目前的代码来看, 这个库目前能确定能做的大概类似:
ini
val appState = AppState()
@Serializable
object NameKey : AppStateKey<String>()
val state = appState.getState(NameKey, "default")
// state.value == "default"
appState.setState(NameKey, "Asher")
// 再 getState(NameKey, "default").value == "Asher"
另外这个库是 Kotlin consumers only ,属于高度 Kotlin/Compose 化,使用了大量 @Composable lambda、泛型 key、Kotlin serialization、object singleton key 等:
swift
AppStateKey<T>
listener: @Composable AppStateToken.(Map<AppStateKey<*>, State<*>>) -> Unit
update: (T) -> T
@Serializable object Key : AppStateKey<String>()
当然,最重要的是,这次修改不只是 Android API ,还有 native bcv:
yaml
Targets: [iosArm64, iosSimulatorArm64, linuxArm64, linuxX64, macosArm64, mingwX64, tvosArm64, ...]
Library unique name: <androidx.appstate:appstate>
同时源码路径是:
bash
src/commonMain/kotlin/androidx/appstate/AppState.kt
src/commonTest/kotlin/androidx/appstate/AppStateTest.kt
也就是,这个项目不是只给 Android JVM 写的,而是 AndroidX Multiplatform 结构下的 commonMain 库,所以这是一个面向 Kotlin Multiplatform 的项目。
我感觉,它不是传统
SavedStateHandle/ViewModel的替代品,而更像是 Compose Runtime 层的状态托管基建。

对比 ViewModel 大概是:
- ViewModel 解决的是 Android 架构里的屏幕级状态和业务逻辑访问
- 而 AppState 更像是,希望某些
MutableState不被某个 Composable 或 Android component 生命周期限制,那么可以用一个外部 AppState 容器托管
所以 AppState 更像是进一步把
MutableState<T>本身放进一个统一容器,注释也明确说beyond individual Android components,这不是"单个 composable 内保存状态",也不是"某个 Activity 内部字段",而是更外层的状态 store。
所以它可能想支持跨组件协作状态?另外 autoClearKey + predicate + listener + LaunchedEffect 这一套设计,看起来更像是当某个关联状态发生变化时,自动清掉另一个状态,感觉可以类似:
ini
object CurrentUserKey : AppStateKey<User?>()
object DraftMessageKey : AppStateKey<String>(
autoClearKey = CurrentUserKey,
predicate = { appState -> true }
)
比如当用户变化时,清理某些跟用户绑定的草稿、缓存、临时 UI 状态 ?当然这只是猜测。
所以,整体看起来,AppState 更像是是 AndroidX 正在引入的一个 Compose Runtime 层状态托管容器:

不过项目目前还处于落地阶段,更详细的实现还没有,但是主方向应该差不多,总的来看,核心就是可以把 Compose 状态提升到组件之外,解决多个 Composable / 多个组件之间共享 Compose State 的问题。