StateStore Một phần của Android Jetpack.
StateStore là một vùng chứa an toàn về kiểu, lưu trữ và hiển thị trạng thái ứng dụng dùng chung dưới dạng State của Compose trên toàn bộ ứng dụng.
Trường hợp sử dụng
Bằng cách tạo StateStore trong phạm vi ứng dụng, bạn có thể chia sẻ trạng thái giữa các thành phần của ứng dụng trong cùng một quy trình:
- Nhiều hoạt động: Nếu ứng dụng của bạn hiển thị nhiều hoạt động cùng lúc (chẳng hạn như hoạt động hộp thư đến và hoạt động trình chỉnh sửa trong ứng dụng email) hoặc có một hoạt động hỗ trợ nhiều phiên bản (chẳng hạn như hoạt động trình xem tài liệu), thì bạn có thể sử dụng
StateStoređể chia sẻ thông tin giữa các hoạt động đó. - Hoạt động và dịch vụ: Đồng bộ hoá trạng thái giữa một hoạt động và một
CarAppServicehoặc AppFunction ở chế độ nền. - Hoạt động và trình cung cấp nội dung hoặc tiện ích: Chia sẻ trạng thái giữa một hoạt động và một tiện ích trên màn hình chính.
Nếu một singleton chỉ cần State<T> trong bộ nhớ mà không cần tra cứu theo khoá hoặc khôi phục khi bị buộc tắt bằng DataStore, thì bạn có thể hiển thị trực tiếp các thuộc tính mutableStateOf trên singleton. Để so sánh các API trạng thái trong ứng dụng, hãy xem phần Chọn API trạng thái phù hợp trong cấu trúc của bạn.
Tổng quan về API
Gói androidx.appstate.statestore cung cấp các lớp StateStore và 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,
)
Xác định trạng thái của bạn
Để đảm bảo an toàn về kiểu khi lưu trữ và truy xuất giá trị, StateStore sử dụng lớp StateStoreKey để xác định các khoá dùng để đọc và ghi giá trị. Giữ các định nghĩa StateStoreKey private hoặc internal cho trình quản lý, kho lưu trữ hoặc mô-đun sở hữu phần trạng thái đó:
@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)
Tạo và chèn các thực thể StateStore
StateStore là một lớp thông thường mà bạn tạo thực thể một lần ở phạm vi ứng dụng hoặc mô-đun (ví dụ: trong lớp Application hoặc mô-đun chèn phần phụ thuộc) và chèn vào các trình quản lý hoặc kho lưu trữ đọc và ghi trạng thái, thay vì hiển thị trực tiếp StateStore cho các lớp giao diện người dùng:
// 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, )
Trạng thái đọc và ghi
Bên trong trình quản lý hoặc kho lưu trữ, hãy đọc, ghi và cập nhật các giá trị trong StateStore được chèn bằng cách sử dụng các định nghĩa StateStoreKey riêng tư, đồng thời hiển thị các thuộc tính State<T> chỉ có thể đọc và các hàm đột biến cho phương thức gọi.
Trạng thái đọc
Sử dụng getState(stateKey, defaultValue) để truy xuất đối tượng State<T>. Nếu khoá chưa được khởi chạy, StateStore sẽ điền khoá đó bằng tham số defaultValue, mặc định là stateKey.defaultValue (hoặc bạn có thể truyền defaultValue tuỳ chỉnh khi khởi chạy khoá chưa được điền). Vì
getState trả về State<T>, nên ViewModel, thành phần kết hợp giao diện người dùng, tiện ích và
khối transform có thể đọc thuộc tính của trình quản lý
theo cách phản ứng:
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}") } }
Trạng thái ghi
Sử dụng setState(stateKey, value) bên trong trình quản lý để khởi tạo hoặc thay thế giá trị liên kết với một khoá:
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) } }
Cập nhật trạng thái
Nếu bạn cần cập nhật trạng thái dựa trên giá trị hiện tại, hãy sử dụng updateState.
Thao tác này sẽ truy xuất giá trị hiện tại (hoặc sử dụng giá trị mặc định nếu chưa đặt) và áp dụng lambda cập nhật của bạn:
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 } } } }
Các công cụ tích hợp
StateStore tích hợp với Jetpack DataStore để duy trì và khôi phục trạng thái trong trường hợp bị buộc tắt và khởi động lại ứng dụng.
DataStore
Cấu phần phần mềm androidx.appstate.statestore:statestore-datastore cho phép bạn định cấu hình tính bền vững một lần trên thực thể StateStore dùng chung để mọi khoá bền vững của trình quản lý đều tự động lưu và khôi phục từ ổ đĩa bằng DataStore.
Gói androidx.appstate.datastore cung cấp các API sau:
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>,
)
- Triển khai
PersistToDataStore: Đánh dấu mọiStateStoreKeycần được duy trì bằng cách triển khai giao diệnPersistToDataStore:
@Serializable private data object FocusSessionKey : StateStoreKey<FocusSession>(defaultValue = FocusSession.Idle), PersistToDataStore
- Liên kết
StateStorevới DataStore: GọisyncToDataStoremột lần khi thiết lập thực thểStateStoredùng chung (ví dụ: trong lớpApplicationhoặc mô-đun chèn phần phụ thuộc). VìsyncToDataStorelà một hàmsuspendtạm ngưng vô thời hạn để khôi phục các giá trị đã lưu từ ổ đĩa và tiếp tục lưu các thay đổi trong tương lai trongcoroutineScopeđược cung cấp, nên hãy gọi hàm này bên tronglaunchvà truyềnCoroutineScopeở cấp ứng dụng. Khi khởi động,getStateban đầu sẽ trả vềdefaultValuetrong bộ nhớ và cập nhậtStatesau khi DataStore đọc xong từ ổ đĩa:
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) }
Nguyên tắc về tính bền vững của DataStore
Vì syncToDataStore chuyển đổi tuần tự và ghi vào ổ đĩa bất cứ khi nào giá trị của khoá PersistToDataStore thay đổi, hãy làm theo các nguyên tắc sau khi chọn nội dung và thời điểm duy trì:
- Lưu dấu thời gian bắt đầu hoặc kết thúc thay vì mọi nhịp hẹn giờ: Đối với bộ hẹn giờ hoặc tiến trình phát, hãy duy trì một giá trị tham chiếu ổn định (chẳng hạn như
endTimeMillishoặcstartedAtMillis) thay vì cập nhật khoáPersistToDataStoremỗi giây hoặc mỗi khung hình, đồng thời tính toán thời gian đã trôi qua hoặc thời gian còn lại trong giao diện người dùng. - Loại bỏ dữ liệu đầu vào nhanh của người dùng trước khi cập nhật khoá cố định: Đối với bản nháp văn bản, cụm từ tìm kiếm hoặc thanh trượt, hãy giữ dữ liệu đầu vào tức thì trong
MutableStatetrong bộ nhớ (hoặcStateStoreKeykhông triển khaiPersistToDataStore) và chỉ gọisetStatetrênStateStoreKeycố định sau khi dữ liệu đầu vào ổn định. - Giữ cho các khoá được duy trì ở trạng thái gọn nhẹ: Tất cả khoá
PersistToDataStoretrongStateStoređều được chuyển đổi tuần tự thành một tệp duy nhất trong mỗi lần cập nhật. Duy trì trạng thái phiên thu gọn (chẳng hạn như mã nhận dạng đang hoạt động, lựa chọn bộ lọc hoặc bản nháp biểu mẫu đang thực hiện) và giữ các tập hợp lớn hoặc tập dữ liệu có thể truy vấn trong Room.