Bermigrasi dari LiveData ke Alur Kotlin

Kami membutuhkan LiveData pada tahun 2017. Pola Observer membuat hidup kami lebih mudah, tetapi opsi seperti RxJava terlalu rumit untuk pemula pada saat itu. Tim Komponen Arsitektur membuat LiveData: kelas penyimpanan data teramati yang sangat otoritatif yang dikembangkan untuk Android. Itu sederhana untuk membuatnya mudah untuk memulai, dan untuk kasus threading reaktif yang lebih kompleks disarankan untuk menggunakan RxJava, mengambil keuntungan dari integrasi antara keduanya.





Data Mati?

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





Flows , ViewModel. , Android, .





, Flows , , . 





Flow: , —

LiveData : , Android. , , .





LiveData Flow:





#1:  

, :





Menampilkan hasil dari satu operasi dengan pemegang data yang dapat diubah (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:





Menampilkan hasil dari satu operasi dengan pemegang data yang dimodifikasi (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:





Menampilkan hasil dari satu operasi (LiveData)
(LiveData)
class MyViewModel(...) : ViewModel() {
    val result: LiveData<Result<UiState>> = liveData {
        emit(Result.Loading)
        emit(repository.fetchItem())
    }
}
      
      



, UI-  - Result



, , Loading



, Success



Error



.





Flow , :





Menampilkan hasil dari satu operasi (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:





Pemuatan data satu kali dengan parameter (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 :





Pemuatan data satu kali dengan parameter (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



:





Menonton streaming dengan parameter (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:





Memantau aliran dengan parameter (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. , , .





Mengumpulkan utas menggunakan peluncuran / peluncuranWhenX tidak aman
launch/launchWhenX

, , , View. , , .





, , StateFlow, ; API.





lifecycle.repeatOnLifecycle

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





Berbagai metode pengumpulan aliran

, :





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



, STARTED



, RESUMED



, STOPPED



. Android.





API repeatOnLifecycle



StateFlow .





StateFlow diekspos menggunakan WhileSubscribed (5000) dan dikumpulkan menggunakan repeatOnLifecycle (MULAI) 
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