StateStore   بخشی از Android Jetpack.

StateStore محفظه‌ای با ایمنی تایپی است که وضعیت برنامه هم‌رسانی‌شده را به‌صورت Compose 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 را مستقیماً در کلاس‌های 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 استفاده کنید. این کار مقدار فعلی را بازیابی می‌کند (یا اگر تنظیم نشده باشد، از مقدار پیش‌فرض استفاده می‌کند) و لامبدای به‌روزرسانی شما را اعمال می‌کند:

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>,
)
  1. پیاده‌سازی PersistToDataStore: هر StateStoreKey را که باید با پیاده‌سازی میانای PersistToDataStore حفظ شود علامت‌گذاری کنید:

@Serializable
private data object FocusSessionKey :
    StateStoreKey<FocusSession>(defaultValue = FocusSession.Idle),
    PersistToDataStore

  1. Bind StateStore to DataStore: Call syncToDataStore once when setting up your shared StateStore instance (for example, in your Application class or dependency injection module). چون 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

چون syncToDataStore هرگاه مقدار کلید PersistToDataStore تغییر کند، آن را سریال‌سازی می‌کند و در دیسک می‌نویسد، هنگام انتخاب اینکه چه چیزی و چه زمانی پایدار بماند، این دستورالعمل‌ها را دنبال کنید:

  • مُهرهای زمان شروع یا پایان را به‌جای هر تیک زمان‌سنج ذخیره کنید: برای زمان‌سنج‌ها یا پیشرفت بازپخش، به‌جای به‌روزرسانی کلید PersistToDataStore در هر ثانیه یا فریم، مقدار مرجع پایداری (مانند endTimeMillis یا startedAtMillis) را حفظ کنید و نمایش زنده زمان سپری‌شده یا باقی‌مانده را در واسط کاربر محاسبه کنید.
  • قبل‌از به‌روزرسانی کلید ماندگار، ورودی سریع کاربر را لرزش‌گیری کنید: برای پیش‌نویس‌های نوشتاری، پُرسمان‌های جستجو، یا لغزنده‌ها، ورودی فوری را در یک MutableState درون‌حافظه‌ای (یا یک StateStoreKey که PersistToDataStore را پیاده‌سازی نمی‌کند) نگه دارید و فقط پس‌از تثبیت ورودی، setState را روی StateStoreKey ماندگار فراخوانی کنید.
  • سبک نگه داشتن کلیدهای ماندگار: همه کلیدهای PersistToDataStore در یک StateStore در هر به‌روزرسانی باهم در یک فایل واحد سریال‌سازی می‌شوند. وضعیت جلسه فشرده را حفظ کنید—مانند شناسه‌های فعال، انتخاب‌های فیلتر، یا پیش‌نویس‌های فرم درحال انجام—و مجموعه‌های بزرگ یا مجموعه‌داده‌های قابل‌پرس‌وجو را در Room نگه دارید.