StateStore Android Jetpack 的一部分。
StateStore 是類型安全的容器,可在整個應用程式中儲存及公開共用應用程式狀態,做為 Compose State。
用途
建立應用程式範圍的 StateStore,即可在同一程序中的應用程式元件之間共用狀態:
- 多項活動:如果應用程式同時顯示多項活動 (例如電子郵件用戶端中的收件匣活動和編輯器活動),或有支援多實體的活動 (例如文件檢視器活動),您可以使用
StateStore在這些活動之間共用資訊。 - 活動和服務:同步處理活動與背景
CarAppService或 AppFunction 之間的狀態。 - Activity 和內容供應器或小工具:在 Activity 和主畫面小工具之間共用狀態。
如果單例模式只需要記憶體內 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 類別或依附元件插入模組) 中例項化一次,並插入讀取和寫入狀態的管理工具或存放區,而不是直接向 UI 類別公開 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、UI 可組合項、小工具和 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鍵,並在 UI 中計算即時經過或剩餘的顯示時間。 - 在更新永久金鑰前,先對快速使用者輸入進行去抖動處理:對於文字草稿、搜尋查詢或滑桿,請將即時輸入保留在記憶體內
MutableState(或未實作PersistToDataStore的StateStoreKey) 中,並在輸入穩定後,才對永久StateStoreKey呼叫setState。 - 保持輕量化的永久金鑰:每次更新時,
StateStore中的所有PersistToDataStore金鑰都會一起序列化為單一檔案。保留精簡的工作階段狀態,例如有效 ID、篩選器選項或進行中的表單草稿,並將大型集合或可查詢的資料集保留在 Room 中。