StateStore เป็นส่วนหนึ่งของ Android Jetpack
StateStore เป็นคอนเทนเนอร์ที่ปลอดภัยในการกำหนดประเภท ซึ่งจัดเก็บและแสดงสถานะแอปพลิเคชันที่แชร์เป็น Compose State ทั่วทั้งแอป
กรณีการใช้งาน
การสร้าง StateStore ที่กำหนดขอบเขตระดับแอปพลิเคชันจะช่วยให้คุณแชร์สถานะระหว่างคอมโพเนนต์ของแอปพลิเคชันภายในกระบวนการเดียวกันได้ ดังนี้
- กิจกรรมหลายรายการ: หากแอปแสดงกิจกรรมหลายรายการพร้อมกัน (เช่น กิจกรรมกล่องจดหมายและกิจกรรมเครื่องมือแก้ไขในโปรแกรมรับส่งอีเมล) หรือมีกิจกรรมที่รองรับอินสแตนซ์หลายรายการ (เช่น กิจกรรมโปรแกรมดูเอกสาร) คุณสามารถใช้
StateStoreเพื่อแชร์ข้อมูลระหว่างกิจกรรมได้ - กิจกรรมและบริการ: ซิงค์สถานะระหว่างกิจกรรมกับ
CarAppServiceหรือ AppFunction ในเบื้องหลัง - กิจกรรมและ Content Provider หรือวิดเจ็ต: แชร์สถานะระหว่างกิจกรรมกับวิดเจ็ตบนหน้าจอหลัก
หากซิงเกิลตันต้องการเพียง 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 หรือโมดูลการแทรกทรัพยากร Dependency) และแทรกลงในตัวจัดการหรือที่เก็บซึ่งอ่านและเขียนสถานะ แทนที่จะแสดง StateStore ต่อคลาส UI โดยตรง
// 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, )
สถานะการอ่านและเขียน
ภายในเครื่องมือจัดการหรือที่เก็บ ให้อ่าน เขียน และอัปเดตค่าใน StateStore ที่แทรกโดยใช้คำจำกัดความ StateStoreKey ส่วนตัว และแสดงพร็อพเพอร์ตี้ State<T> แบบอ่านอย่างเดียวและฟังก์ชันการเปลี่ยนแปลงต่อผู้เรียก
สถานะการอ่าน
ใช้ getState(stateKey, defaultValue) เพื่อดึงข้อมูลออบเจ็กต์ State<T> หากยังไม่ได้เริ่มต้นคีย์ StateStore จะป้อนข้อมูลด้วยพารามิเตอร์ defaultValue ซึ่งมีค่าเริ่มต้นเป็น stateKey.defaultValue (หรือคุณสามารถส่ง defaultValue ที่กำหนดเองเมื่อเริ่มต้นคีย์ที่ไม่มีข้อมูล) เนื่องจาก getState แสดงผล State<T>, ViewModels, UI composables, วิดเจ็ต และบล็อก 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 ช่วยให้คุณกำหนดค่าความคงอยู่ได้ 1 ครั้งในอินสแตนซ์ 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: ทำเครื่องหมายStateStoreKeyที่ควรคงอยู่โดยใช้PersistToDataStoreinterface:
@Serializable private data object FocusSessionKey : StateStoreKey<FocusSession>(defaultValue = FocusSession.Idle), PersistToDataStore
- เชื่อมโยง
StateStoreกับ DataStore: เรียกใช้syncToDataStore1 ครั้งเมื่อตั้งค่าอินสแตนซ์StateStoreที่ใช้ร่วมกัน (เช่น ในคลาสApplicationหรือโมดูลการแทรกทรัพยากร Dependency) เนื่องจากsyncToDataStoreเป็นฟังก์ชันsuspendที่ระงับอย่างไม่มีกำหนดเพื่อกู้คืนค่าที่บันทึกไว้จากดิสก์และบันทึกการเปลี่ยนแปลงในอนาคตต่อไปในcoroutineScopeที่ระบุ ให้เรียกใช้ฟังก์ชันนี้ภายในlaunchและส่งCoroutineScopeระดับแอปพลิเคชัน เมื่อเริ่มต้นระบบgetStateจะแสดงผลdefaultValueในหน่วยความจำในตอนแรก และอัปเดตStateเมื่อ DataStore อ่านจากดิสก์เสร็จแล้ว
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
เนื่องจาก syncToDataStore จะจัดเรียงและเขียนลงในดิสก์ทุกครั้งที่ค่าของคีย์ PersistToDataStore มีการเปลี่ยนแปลง โปรดทำตามหลักเกณฑ์ต่อไปนี้เมื่อเลือกสิ่งที่จะคงอยู่และเวลาที่จะคงอยู่
- บันทึกการประทับเวลาเริ่มต้นหรือสิ้นสุดแทนการบันทึกทุกครั้งที่ตัวจับเวลาเดิน: สำหรับตัวจับเวลาหรือความคืบหน้าในการเล่น ให้คงค่าอ้างอิงที่เสถียรไว้ (เช่น
endTimeMillisหรือstartedAtMillis) แทนการอัปเดตคีย์PersistToDataStoreทุกวินาทีหรือทุกเฟรม และคำนวณเวลาที่ผ่านไปหรือเวลาที่เหลืออยู่แบบเรียลไทม์ใน UI - ลดการป้อนข้อมูลของผู้ใช้อย่างรวดเร็วก่อนอัปเดตคีย์ที่เก็บไว้: สำหรับข้อความร่าง คำค้นหา หรือแถบเลื่อน ให้เก็บข้อมูลที่ป้อนทันทีไว้ใน
MutableStateในหน่วยความจำ (หรือStateStoreKeyที่ไม่ได้ใช้PersistToDataStore) และเรียกใช้setStateในStateStoreKeyที่เก็บไว้หลังจากที่ข้อมูลที่ป้อนคงที่แล้วเท่านั้น - ทำให้คีย์ที่คงอยู่มีขนาดเล็ก: คีย์
PersistToDataStoreทั้งหมดในStateStoreจะได้รับการจัดเรียงเป็นอนุกรมร่วมกันเป็นไฟล์เดียวในการอัปเดตแต่ละครั้ง คงสถานะเซสชันแบบกะทัดรัดไว้ เช่น รหัสที่ใช้งานอยู่ การเลือกตัวกรอง หรือแบบร่างแบบฟอร์มที่อยู่ระหว่างดำเนินการ และเก็บคอลเล็กชันขนาดใหญ่หรือชุดข้อมูลที่ค้นหาได้ไว้ใน Room