LiveData から Kotlin のフローへの移行

2017 年には LiveData が必要でした。Observer パターンは私たちの生活を楽にしてくれましたが、RxJava のようなオプションは当時の初心者には複雑すぎました。Architecture Components チームは LiveData を作成しました。これは、Android 向けに開発された非常に信頼できる監視可能なデータ ストア クラスです。開始を容易にするのは簡単で、より複雑なリアクティブ スレッドの場合には、RxJava と RxJava の統合を利用して、RxJava を使用することが推奨されました。





デッドデータ?

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





Flows , ViewModel. , Android, .





, Flows , , . 





Flow: , —

LiveData : , Android. , , .





LiveData Flow:





#1:  

, :





Mutable データ ホルダー (LiveData) を使用した 1 回の操作の結果の表示
(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:





変更されたデータ ホルダー (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:





1 回の操作の結果の表示 (LiveData)
(LiveData)
class MyViewModel(...) : ViewModel() {
    val result: LiveData<Result<UiState>> = liveData {
        emit(Result.Loading)
        emit(repository.fetchItem())
    }
}
      
      



, UI-  - Result



, , Loading



, Success



Error



.





Flow , :





単一の操作の結果を表示する (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:





パラメータを使用したワンタイム データの読み込み (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 :





パラメータを使用した 1 回限りのデータ ロード (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



:





パラメータ付きのストリームの視聴 (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:





パラメータによるフローのモニタリング (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. , , .





launch / launchWhenX を使用したスレッドの収集は安全ではありません
launch/launchWhenX

, , , 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 は WhileSubscribe (5000) を使用して公開され、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