Migración de LiveData a Kotlin's Flow

LiveData 2017 . , , RxJava, . Architecture Components LiveData: , Android. , , RxJava, .





DeadData?

LiveData - Java-, . , Kotlin Flows. Flows () , Kotlin, Jetbrains; , Compose, .





Flows , ViewModel. , Android, .





, Flows , , . 





Flow: , —

LiveData : , Android. , , .





LiveData Flow:





#1:  

, :





Visualización del resultado de una sola operación con un contenedor de datos mutable (LiveData)
(Mutable) (LiveData)
<!-- 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:





Visualización del resultado de una sola operación con un contenedor de datos modificado (StateFlow)
  (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
        }
    }
}
      
      



StateFlowSharedFlow ( Flow), LiveData:





  • .





  • .





  • ( ).





  • , .





StateFlow. , .





#2:

, .





LiveData liveData:





Visualización del resultado de una sola operación (LiveData)
(LiveData)
class MyViewModel(...) : ViewModel() {
    val result: LiveData<Result<UiState>> = liveData {
        emit(Result.Loading)
        emit(repository.fetchItem())
    }
}
      
      



, UI-  - Result



, , Loading



, Success



Error



.





Flow , :





Visualización del resultado de una sola operación (StateFlow)
(StateFlow)
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:





Carga de datos única con parámetros (LiveData)
(LiveData)

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 :





Carga de datos única con parámetros (StateFlow)
(StateFlow)
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



:





Ver una transmisión con parámetros (LiveData)
(LiveData)
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:





Monitoreo de flujo con parámetros (StateFlow)
(StateFlow)
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 }
      
      



combineTransform zip.





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. , , .





Recopilar hilos mediante launch / launchWhenX no es seguro
launch/launchWhenX

, , , View. , , .





, , StateFlow, ; API.





lifecycle.repeatOnLifecycle

( lifecycle-runtime-ktx 2.4.0-alpha01) , : , .





Varios métodos de recopilación de un flujo.

, :





onCreateView(...) {
    viewLifecycleOwner.lifecycleScope.launch {
        viewLifecycleOwner.lifecycle.repeatOnLifecycle(STARTED) {
            myViewModel.myUiState.collect { ... }
        }
    }
}
      
      



, STARTED



, RESUMED



, STOPPED



. Android.





API repeatOnLifecycle



StateFlow .





StateFlow se expone mediante WhileSubscriptions (5000) y se recopila mediante repeatOnLifecycle (STARTED) 
StateFlow WhileSubscribed(5000) repeatOnLifecycle(STARTED) 

: StateFlow, Data Binding, launchWhenCreated



, repeatOnLifecycle



` , .





Data Binding Flows asLiveData()



, . , lifecycle-runtime-ktx 2.4.0



.





ViewModel :





  • StateFlow



    , WhileSubscribed



    , . []





  • repeatOnLifecycle



    . [].





, :






"Android Developer. Basic". , . , .








All Articles