StateStore Android Jetpack 的一部分。
StateStore 是一种类型安全的容器,可在整个应用中以 Compose State 的形式存储和公开共享的应用状态。
使用场景
通过创建应用级作用域的 StateStore,您可以在同一进程内的应用组件之间共享状态:
- 多个 activity:如果您的应用同时显示多个 activity(例如电子邮件客户端中的收件箱 activity 和编辑器 activity),或者具有支持多实例的 activity(例如文档查看器 activity),您可以使用
StateStore在它们之间共享信息。 - activity 和服务:在 activity 与后台
CarAppService或 AppFunction 之间同步状态。 - activity 和 content provider 或 widget:在 activity 和主屏幕 widget 之间共享状态。
如果单例只需要内存中的 State<T>,而不需要键控查找或 DataStore 支持的进程终止恢复,您可以改为直接在单例上公开 mutableStateOf 属性。如需比较应用中的状态 API,请参阅在架构中选择合适的状态 API。
API 概览
androidx.appstate.statestore 软件包提供 StateStore 和 StateStoreKey 类:
package androidx.appstate.statestore
import androidx.compose.runtime.State
import kotlinx.serialization.Serializable
public class StateStore {
public val keys: Set<StateStoreKey<*>>
public fun <T> getState(
stateKey: StateStoreKey<T>,
defaultValue: T = stateKey.defaultValue,
): State<T>
public fun <T> setState(stateKey: StateStoreKey<T>, value: T)
public inline fun <T> updateState(
stateKey: StateStoreKey<T>,
defaultValue: T = stateKey.defaultValue,
update: (T) -> T,
)
}
@Serializable
public open class StateStoreKey<T>(
public val defaultValue: T,
)
定义状态
为了确保在存储和检索值时类型安全,StateStore 使用 StateStoreKey 类来定义用于读取和写入值的键。将 StateStoreKey 定义 private 或 internal 保留在拥有该状态切片的管理器、代码库或模块中:
@Serializable sealed interface FocusSession { @Serializable data object Idle : FocusSession @Serializable data class Running( val taskId: String, val endTimeMillis: Long, ) : FocusSession @Serializable data class Paused( val taskId: String, val remainingMillis: Long, ) : FocusSession } @Serializable private data object FocusSessionKey : StateStoreKey<FocusSession>(defaultValue = FocusSession.Idle)
创建和注入 StateStore 实例
StateStore 是一个常规类,您可以在应用或模块范围(例如,在 Application 类或依赖项注入模块中)实例化一次,并注入到读取和写入状态的管理器或代码库中,而不是直接向界面类公开 StateStore:
// Instantiate at the application or DI module level val stateStore = StateStore() // Inject into managers or repositories that encapsulate their own keys class FocusSessionManager( private val stateStore: StateStore, )
读写状态
在管理器或代码库中,使用私有 StateStoreKey 定义读取、写入和更新注入的 StateStore 中的值,并向调用方公开只读 State<T> 属性和变更函数。
阅读状态
使用 getState(stateKey, defaultValue) 检索 State<T> 对象。如果密钥尚未初始化,StateStore 会使用 defaultValue 参数(默认为 stateKey.defaultValue)填充该密钥(或者您可以在初始化未填充的密钥时传递自定义 defaultValue)。由于 getState 会返回 State<T>,因此 ViewModel、界面可组合项、微件和 transform 代码块可以被动读取管理器的属性:
class FocusSessionManager( private val stateStore: StateStore, ) { val session: State<FocusSession> = stateStore.getState(FocusSessionKey) } class FocusTimerViewModel( focusSessionManager: FocusSessionManager, ) : ViewModel() { val session: State<FocusSession> = focusSessionManager.session } @Composable fun FocusBanner(viewModel: FocusTimerViewModel = viewModel()) { // Reads state reactively; recomposes when FocusSessionKey updates when (val session = viewModel.session.value) { FocusSession.Idle -> Text(text = "No active focus session") is FocusSession.Running -> Text(text = "Focusing on ${session.taskId}") is FocusSession.Paused -> Text(text = "Paused: ${session.taskId}") } }
写入状态
在管理器中使用 setState(stateKey, value) 初始化或替换与键关联的值:
class FocusSessionManager( private val stateStore: StateStore, ) { val session: State<FocusSession> = stateStore.getState(FocusSessionKey) fun startFocus(taskId: String, endTimeMillis: Long) { stateStore.setState( FocusSessionKey, FocusSession.Running( taskId = taskId, endTimeMillis = endTimeMillis, ), ) } fun endFocus() { stateStore.setState(FocusSessionKey, FocusSession.Idle) } }
更新状态
如果您需要根据当前值更新状态,请使用 updateState。
这会检索当前值(如果未设置,则使用默认值),并应用更新 lambda:
class FocusSessionManager( private val stateStore: StateStore, ) { val session: State<FocusSession> = stateStore.getState(FocusSessionKey) fun pauseFocus(nowMillis: Long) { stateStore.updateState(FocusSessionKey) { current -> if (current is FocusSession.Running) { FocusSession.Paused( taskId = current.taskId, remainingMillis = (current.endTimeMillis - nowMillis) .coerceAtLeast(0L), ) } else { current } } } fun resumeFocus(nowMillis: Long) { stateStore.updateState(FocusSessionKey) { current -> if (current is FocusSession.Paused) { FocusSession.Running( taskId = current.taskId, endTimeMillis = nowMillis + current.remainingMillis, ) } else { current } } } }
集成
StateStore 与 Jetpack DataStore 集成,可在进程终止和应用重启后持久保存和恢复状态。
DataStore
借助 androidx.appstate.statestore:statestore-datastore 工件,您可以在共享 StateStore 实例上配置一次持久性,这样任何管理器的持久化密钥都会自动使用 DataStore 从磁盘保存和恢复。
androidx.appstate.datastore 软件包提供以下 API:
package androidx.appstate.datastore
import androidx.appstate.statestore.StateStore
import androidx.datastore.core.DataStore
import kotlinx.coroutines.CoroutineScope
public interface PersistToDataStore
public suspend fun StateStore.syncToDataStore(
path: String,
coroutineScope: CoroutineScope,
)
public suspend fun StateStore.syncToDataStore(
dataStore: DataStore<StateStorePreferences>,
)
- 实现
PersistToDataStore:通过实现PersistToDataStore接口,标记应保留的所有StateStoreKey:
@Serializable private data object FocusSessionKey : StateStoreKey<FocusSession>(defaultValue = FocusSession.Idle), PersistToDataStore
- 将
StateStore绑定到 DataStore:在设置共享StateStore实例时(例如,在Application类或依赖项注入模块中),调用syncToDataStore一次。由于syncToDataStore是一个suspend函数,它会无限期挂起,以从磁盘恢复已保存的值,并继续在提供的coroutineScope中保存未来的更改,因此请在launch内调用它,并传递应用级CoroutineScope。启动时,getState最初会在内存中返回defaultValue,并在 DataStore 完成从磁盘读取后更新State:
class ProductivityApplication : Application() { val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) val stateStore = StateStore().also { store -> // Restore saved values and automatically persist future changes // for all PersistToDataStore keys across managers applicationScope.launch { store.syncToDataStore( path = dataStoreFile("statestore.json").absolutePath, coroutineScope = applicationScope, ) } } val focusSessionManager = FocusSessionManager(stateStore) }
DataStore 持久化指南
由于每当 PersistToDataStore 键的值发生更改时,syncToDataStore 都会序列化并写入磁盘,因此在选择要持久保存的内容和时间时,请遵循以下准则:
- 保存开始或结束时间戳,而不是每个计时器滴答:对于计时器或播放进度,请持久保存稳定的参考值(例如
endTimeMillis或startedAtMillis),而不是每秒或每帧更新一次PersistToDataStore键,并在界面中计算实时经过或剩余的显示时间。 - 在更新持久化键之前,对快速用户输入进行去抖动处理:对于文本草稿、搜索查询或滑块,请将即时输入保存在内存中的
MutableState(或未实现PersistToDataStore的StateStoreKey)中,并且仅在输入稳定后对持久化StateStoreKey调用setState。 - 使持久化键保持轻量:每次更新时,
StateStore中的所有PersistToDataStore键都会一起序列化到单个文件中。持久保存紧凑的会话状态(例如活跃 ID、过滤条件选择或正在填写的表单草稿),并将大型集合或可查询的数据集保留在 Room 中。