We needed LiveData back in 2017. The Observer pattern made life easier for us, but options like RxJava were too complex for beginners at the time. The Architecture Components team created LiveData: a very authoritative observable data store class developed for Android. It was simple to make it easy to get started, and for more complex reactive threading cases it was recommended to use RxJava, taking advantage of the integration between the two.
DeadData?
LiveData - Java-, . , Kotlin Flows. Flows () , Kotlin, Jetbrains; , Compose, .
Flows , ViewModel. , Android, .
, Flows , , .
Flow: , —
LiveData : , Android. , , .
LiveData Flow:
#1:
, :
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class MyViewModel {
private val _myUiState = MutableLiveData<Result<UiState>>(Result.Loading)
val myUiState: LiveData<Result<UiState>> = _myUiState
// Load data from a suspend fun and mutate state
init {
viewModelScope.launch {
val result = ...
_myUiState.value = result
}
}
}
, StateFlow:
class MyViewModel {
private val _myUiState = MutableStateFlow<Result<UiState>>(Result.Loading)
val myUiState: StateFlow<Result<UiState>> = _myUiState
// Load data from a suspend fun and mutate state
init {
viewModelScope.launch {
val result = ...
_myUiState.value = result
}
}
}
StateFlow — SharedFlow ( Flow), LiveData:
.
.
( ).
, .
StateFlow. , .
#2:
, .
LiveData liveData:
class MyViewModel(...) : ViewModel() {
val result: LiveData<Result<UiState>> = liveData {
emit(Result.Loading)
emit(repository.fetchItem())
}
}
, UI- - Result
, , Loading
, Success
Error
.
Flow , :
class MyViewModel(...) : ViewModel() {
val result: StateFlow<Result<UiState>> = flow {
emit(repository.fetchItem())
}.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5000), // Or Lazily because it's a one-shot
initialValue = Result.Loading
)
}
stateIn
— Flow, StateFlow
. , .
#3:
, , ID , AuthManager
, Flow:
LiveData :
class MyViewModel(authManager..., repository...) : ViewModel() {
private val userId: LiveData<String?> =
authManager.observeUser().map { user -> user.id }.asLiveData()
val result: LiveData<Result<Item>> = userId.switchMap { newUserId ->
liveData { emit(repository.fetchItem(newUserId)) }
}
}
switchMap
— , , userId
.
, userId
LiveData, Flow LiveData.
class MyViewModel(authManager..., repository...) : ViewModel() {
private val userId: Flow<UserId> = authManager.observeUser().map { user -> user.id }
val result: LiveData<Result<Item>> = userId.mapLatest { newUserId ->
repository.fetchItem(newUserId)
}.asLiveData()
}
Flows :
class MyViewModel(authManager..., repository...) : ViewModel() {
private val userId: Flow<UserId> = authManager.observeUser().map { user -> user.id }
val result: StateFlow<Result<Item>> = userId.mapLatest { newUserId ->
repository.fetchItem(newUserId)
}.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5000),
initialValue = Result.Loading
)
}
, , transformLatest
emit
:
val result = userId.transformLatest { newUserId ->
emit(Result.LoadingData)
emit(repository.fetchItem(newUserId))
}.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5000),
initialValue = Result.LoadingUser // Note the different Loading states
)
#4:
. , , .
: fetchItem
observeItem
, Flow.
LiveData LiveData emitSource
:
class MyViewModel(authManager..., repository...) : ViewModel() {
private val userId: LiveData<String?> =
authManager.observeUser().map { user -> user.id }.asLiveData()
val result = userId.switchMap { newUserId ->
repository.observeItem(newUserId).asLiveData()
}
}
, , flatMapLatest LiveData:
class MyViewModel(authManager..., repository...) : ViewModel() {
private val userId: Flow<String?> =
authManager.observeUser().map { user -> user?.id }
val result: LiveData<Result<Item>> = userId.flatMapLatest { newUserId ->
repository.observeItem(newUserId)
}.asLiveData()
}
Flow , LiveData:
class MyViewModel(authManager..., repository...) : ViewModel() {
private val userId: Flow<String?> =
authManager.observeUser().map { user -> user?.id }
val result: StateFlow<Result<Item>> = userId.flatMapLatest { newUserId ->
repository.observeItem(newUserId)
}.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5000),
initialValue = Result.LoadingUser
)
}
StateFlow , .
#5 : MediatorLiveData -> Flow.combine
MediatorLiveData ( LiveData) - , . MediatorLiveData:
val liveData1: LiveData<Int> = ...
val liveData2: LiveData<Int> = ...
val result = MediatorLiveData<Int>()
result.addSource(liveData1) { value ->
result.setValue(liveData1.value ?: 0 + (liveData2.value ?: 0))
}
result.addSource(liveData2) { value ->
result.setValue(liveData1.value ?: 0 + (liveData2.value ?: 0))
}
Flow :
val flow1: Flow<Int> = ...
val flow2: Flow<Int> = ...
val result = combine(flow1, flow2) { a, b -> a + b }
StateFlow ( stateIn)
stateIn
StateFlow, . -, :
val result: StateFlow<Result<UiState>> = someFlow
.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5000),
initialValue = Result.Loading
)
, , 5- started
, .
StateIn
3 ( ):
@param scope the coroutine scope in which sharing is started.
@param started the strategy that controls when sharing is started and stopped.
@param initialValue the initial value of the state flow.
This value is also used when the state flow is reset using the [SharingStarted.WhileSubscribed] strategy with the `replayExpirationMillis` parameter.
started
3 :
Lazily
: , , , scope .
Eagerly
: , scope .
WhileSubscribed
: .
Lazily
Eagerly
. , , WhileSubscribed
, , .
WhileSubscribed
WhileSubscribed , . StateFlow, stateIn
, View, , ( ). , , , , .. , , .
WhileSubscribed
:
public fun WhileSubscribed(
stopTimeoutMillis: Long = 0,
replayExpirationMillis: Long = Long.MAX_VALUE
)
:
stopTimeoutMillis
( ) . ( ).
, , . — , .
liveData 5 , , . WhileSubscribed(5000)
:
class MyViewModel(...) : ViewModel() {
val result = userId.mapLatest { newUserId ->
repository.observeItem(newUserId)
}.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5000),
initialValue = Result.Loading
)
}
:
, , , , .
, , , .
, , , .
replayExpirationMillis
— ( ) (shareIn
initialValue
stateIn
).Long.MAX_VALUE
( , ). .
StateFlow
, StateFlows ViewModel, . , , , .
, . :
Activity.lifecycleScope.launch
: .
Fragment.lifecycleScope.launch
: .
LaunchWhenStarted, launchWhenResumed...
launch, launchWhenX
, , lifecycleOwner
X, , lifecycleOwner
X. , , .
, , , View. , , .
, , StateFlow, ; API.
lifecycle.repeatOnLifecycle
( lifecycle-runtime-ktx 2.4.0-alpha01) , : , .
, :
onCreateView(...) { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycle.repeatOnLifecycle(STARTED) { myViewModel.myUiState.collect { ... } } } }
, STARTED
, RESUMED
, STOPPED
. Android.
API repeatOnLifecycle
StateFlow .
: StateFlow, Data Binding,
launchWhenCreated
,repeatOnLifecycle
` , .
Data Binding Flows
asLiveData()
, . ,lifecycle-runtime-ktx 2.4.0
.
ViewModel :
, :
"Android Developer. Basic". , . , .