StateStore Android Jetpack의 구성요소
StateStore는 공유 애플리케이션 상태를 앱 전체에서 Compose State로 저장하고 노출하는 유형 안전 컨테이너입니다.
사용 사례
애플리케이션 범위 StateStore를 만들면 동일한 프로세스 내에서 애플리케이션 구성요소 간에 상태를 공유할 수 있습니다.
- 여러 활동: 앱이 여러 활동을 동시에 표시하거나 (예: 이메일 클라이언트의 받은편지함 활동 및 편집기 활동) 멀티 인스턴스를 지원하는 활동이 있는 경우(예: 문서 뷰어 활동)
StateStore를 사용하여 활동 간에 정보를 공유할 수 있습니다. - 활동 및 서비스: 활동과 백그라운드
CarAppService또는 AppFunction 간에 상태를 동기화합니다. - Activity 및 콘텐츠 제공자 또는 위젯: Activity와 홈 화면 위젯 간에 상태를 공유합니다.
싱글톤에 키 조회 또는 DataStore 지원 프로세스 종료 복원이 없는 메모리 내 State<T>만 필요한 경우 대신 싱글톤에 직접 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 클래스 또는 종속 항목 삽입 모듈)에서 한 번 인스턴스화하고 상태를 읽고 쓰는 관리자 또는 저장소에 삽입하는 일반 클래스입니다. 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, )
읽기 및 쓰기 상태
관리자 또는 저장소 내에서 비공개 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를 사용합니다.
이렇게 하면 현재 값을 가져오거나 (설정되지 않은 경우 기본값 사용) 업데이트 람다를 적용합니다.
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는 디스크에서 저장된 값을 복원하고 제공된coroutineScope에서 향후 변경사항을 계속 저장하기 위해 무기한 정지하는suspend함수이므로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)을 유지하고 UI에서 실시간 경과 시간 또는 남은 시간 표시를 계산합니다. - 지속 키를 업데이트하기 전에 빠른 사용자 입력 디바운스: 텍스트 초안, 검색어 또는 슬라이더의 경우 즉각적인 입력을 메모리 내
MutableState(또는PersistToDataStore를 구현하지 않는StateStoreKey)에 보관하고 입력이 안정된 후에만 지속StateStoreKey에서setState를 호출합니다. - 지속 키를 가볍게 유지:
StateStore의 모든PersistToDataStore키는 업데이트할 때마다 단일 파일로 함께 직렬화됩니다. 활성 ID, 필터 선택, 진행 중인 양식 초안과 같은 간단한 세션 상태를 유지하고 대규모 컬렉션 또는 쿼리 가능한 데이터 세트를 Room에 보관합니다.