StateStore جزء من Android Jetpack.
StateStore هي حاوية تمنع أخطاء الكتابة وتخزّن حالة التطبيق المشتركة وتعرضها على شكل State في تطبيقك.
حالات الاستخدام
من خلال إنشاء StateStore على مستوى التطبيق، يمكنك مشاركة الحالة بين
مكوّنات التطبيق ضمن العملية نفسها:
- الأنشطة المتعددة: إذا كان تطبيقك يعرض أنشطة متعددة
في الوقت نفسه (مثل نشاط البريد الوارد ونشاط المحرِّر في برنامج
بريد إلكتروني) أو يتضمّن نشاطًا يتيح فتح نسختين من التطبيق
(مثل نشاط عارض المستندات)، يمكنك استخدام
StateStoreلمشاركة المعلومات بينها. - النشاط والخدمة: يمكنك مزامنة الحالة بين نشاط و
CarAppServiceفي الخلفية أو AppFunction. - النشاط وأداة توفير المحتوى أو التطبيق المصغّر: يمكنك مشاركة الحالة بين نشاط وتطبيق مصغّر على الشاشة الرئيسية.
إذا كان الكائن الأحادي يحتاج فقط إلى State<T> في الذاكرة بدون البحث باستخدام مفتاح أو
استعادة البيانات بعد إيقاف العملية نهائيًا من خلال DataStore، يمكنك عرض خصائص mutableStateOf
مباشرةً على الكائن الأحادي بدلاً من ذلك. للمقارنة بين واجهات برمجة التطبيقات الخاصة بالحالة
في تطبيقك، يُرجى الاطّلاع على اختيار واجهة برمجة التطبيقات المناسبة للحالة في
بنية تطبيقك.
نظرة عامة على واجهة برمجة التطبيقات
توفّر حزمة 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, )
حالة القراءة والكتابة
داخل المدير أو المستودع، يمكنك قراءة القيم وكتابتها وتعديلها في
StateStore الذي تم إدخاله باستخدام تعريفات StateStoreKey الخاصة بك، وعرض
خصائص State<T> بإذن بالقراءة فقط ودوال التغيير للمتصلين.
حالة القراءة
استخدِم getState(stateKey, defaultValue) لاسترداد عنصر State<T>. إذا لم تتم تهيئة المفتاح بعد، يملأه StateStore باستخدام المَعلمة defaultValue التي يتم ضبطها تلقائيًا على stateKey.defaultValue (أو يمكنك تمرير defaultValue مخصّصة عند تهيئة مفتاح غير ممتلئ). بما أنّ
getState تعرض State<T>، يمكن لـ ViewModels والدوال المركّبة لواجهة المستخدم والتطبيقات المصغّرة ومجموعات بيانات
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 واجهات برمجة التطبيقات التالية:
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يجب الاحتفاظ به من خلال تنفيذ واجهةPersistToDataStore:
@Serializable private data object FocusSessionKey : StateStoreKey<FocusSession>(defaultValue = FocusSession.Idle), PersistToDataStore
- ربط
StateStoreبـ DataStore: يمكنك استدعاءsyncToDataStoreمرة واحدة عند إعداد مثيلStateStoreالمشترك (على سبيل المثال، في فئةApplicationأو وحدة حقن التبعية). بما أنّ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كل ثانية أو إطار، واحتساب الوقت المنقضي أو المتبقي في واجهة المستخدم. - تجنُّب الإدخال السريع للمستخدم قبل تعديل مفتاح محفوظ: بالنسبة إلى مسودات النصوص أو طلبات البحث أو أشرطة التمرير، يمكنك الاحتفاظ بالإدخال الفوري في
MutableStateفي الذاكرة (أوStateStoreKeyلا ينفّذPersistToDataStore) واستدعاءsetStateفقط علىStateStoreKeyالمحفوظ بعد استقرار الإدخال. - الحفاظ على خفة وزن المفاتيح الثابتة: يتم إجراء تسلسل لجميع مفاتيح
PersistToDataStoreفيStateStoreمعًا في ملف واحد في كل تحديث. يمكنك الاحتفاظ بحالة الجلسة المدمجة، مثل أرقام التعريف النشطة أو اختيارات الفلاتر أو مسودات النماذج قيد التقدم، والاحتفاظ بالمجموعات الكبيرة أو مجموعات البيانات القابلة للاستعلام في Room.