Upgrade to Pro — share decks privately, control downloads, hide ads and more …

LiveData with Coroutines and Flow

Uchun Lee
December 21, 2019

LiveData with Coroutines and Flow

View 와 ViewModel 에서 Coroutine을 보다 손쉽게 사용할 수 있는 extension property에 대해서 알아보고, LiveData coroutine builder도 알아보겠습니다.

References

LiveData with Coroutines and Flow (Android Dev Summit '19)
https://www.youtube.com/watch?v=B8ppnjGPAGE&t=416s

LiveData Overview
https://developer.android.com/topic/libraries/architecture/livedata

Use Kotlin coroutines with Architecture components
https://developer.android.com/topic/libraries/architecture/coroutines

Easy Coroutines in Android: viewModelScope
https://medium.com/androiddevelopers/easy-coroutines-in-android-viewmodelscope-25bffb605471

Lessons learnt using Coroutines Flow in the Android Dev Summit 2019 app
https://medium.com/androiddevelopers/lessons-learnt-using-coroutines-flow-4a6b285c0d06

iosched adssched2019 branch
https://github.com/google/iosched/tree/adssched2019

CodeLab : Advanced Coroutines with Kotlin Flow and LiveData

https://codelabs.developers.google.com/codelabs/advanced-kotlin-coroutines/index.html?index=..%2F..index#0

Testing Coroutines on Android (Android Dev Summit '19)
https://www.youtube.com/watch?v=KMb0Fs8rCRs&t=11s

Cold flows, hot channels
https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9

Simple design of Kotlin Flow
https://medium.com/@elizarov/simple-design-of-kotlin-flow-4725e7398c4c

Kotlin Flows and Coroutines
https://medium.com/@elizarov/kotlin-flows-and-coroutines-256260fb3bdb

Uchun Lee

December 21, 2019
Tweet

Other Decks in Programming

Transcript

  1. dependencies { implementation knowledgebase('Android Architecture Component') // LiveData, ViewModel implementation

    'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0' // viewModelScope implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-rc03' // lifecycleScope implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-rc03' // liveData coroutine builder = CoroutineLiveData // Flow ... Summary
  2. Android Dev Summit 2019 extended Seoul Layered architecture View ViewModel

    LiveData Repository suspend fun DataSource suspend fun UseCase suspend fun Presentation Domain Data
  3. Android Dev Summit 2019 extended Seoul LiveData View ViewModel LiveData

    UseCase suspend fun Repository suspend fun DataSource suspend fun
  4. Android Dev Summit 2019 extended Seoul DataSource suspend fun Coroutine

    View ViewModel LiveData UseCase suspend fun Repository suspend fun
  5. class MyViewModel : ViewModel(), CoroutineScope { override val coroutineContext: CoroutineContext

    get() = SupervisorJob() + Dispatchers.Main fun doSomething() { launch { // do something } } } ViewModel
  6. class MyViewModel : ViewModel(), CoroutineScope { override val coroutineContext: CoroutineContext

    get() = SupervisorJob() + Dispatchers.Main override fun onCleared() { cancel() // coroutineContext.cancel() } } ViewModel
  7. class MyViewModel : ViewModel() { init { viewModelScope.launch { //

    do something } } } ViewModel with viewModelScope
  8. ViewModel with viewModelScope val ViewModel.viewModelScope: CoroutineScope get() { val scope:

    CoroutineScope? = this.getTag(JOB_KEY) if (scope != null) { return scope } return setTagIfAbsent(JOB_KEY, CloseableCoroutineScope( SupervisorJob() + Dispatchers.Main )) }
  9. internal class CloseableCoroutineScope( context: CoroutineContext ) : Closeable, CoroutineScope {

    override val coroutineContext: CoroutineContext = context override fun close() { coroutineContext.cancel() } ViewModel with viewModelScope
  10. ViewModel with viewModelScope public abstract class ViewModel { final void

    clear() { mCleared = true; if (mBagOfTags != null) { synchronized (mBagOfTags) { for (Object value : mBagOfTags.values()) { closeWithRuntimeException(value); } } } onCleared(); }
  11. class MyViewModel : ViewModel() { override fun onCleared() { viewModelScope.launch

    { // do something } } } ViewModel with viewModelScope
  12. class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) lifecycleScope.launch { // do something } } } Activities & Fragments with lifecycleScope
  13. val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope get() = lifecycle.coroutineScope val Lifecycle.coroutineScope: LifecycleCoroutineScope get()

    { ... val newScope = LifecycleCoroutineScopeImpl( this, SupervisorJob() + Dispatchers.Main.immediate ) ... } Activities & Fragments with lifecycleScope
  14. class MyActivity : AppCompatActivity() { override fun onDestroy() { super.onDestroy()

    lifecycleScope.launch { // do something } } } Activities & Fragments with lifecycleScope
  15. class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) lifecycleScope.launch { // do something } lifecycleScope.launchWhenResumed { // do something } } } Activities & Fragments with lifecycleScope
  16. Activities & Fragments with lifecycleScope class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) { ... lifecycleScope.launch { lifecycleScope.launchWhenResumed { repeat(100) { delay(30) textView.text = (it + 1).toString() } }
  17. class MyFragment : Fragment() { init { lifecycleScope.launchWhenCreated { //

    do something } lifecycleScope.launchWhenStarted { // do something } } } Activities & Fragments with lifecycleScope
  18. ViewModel + LiveData class MyViewModel : ViewModel() { private val

    _result = MutableLiveData<String>() val result: LiveData<String> = _result init { viewModelScope.launch { val computationResult = doComputation() _result.value = computationResult } } }
  19. ViewModel + LiveData class MyViewModel : ViewModel() { private val

    _result = MutableLiveData<String>() val result: LiveData<String> = _result init { viewModelScope.launch(Dispatchers.IO) { val computationResult = doComputation() _result.postValue(computationResult) } } }
  20. liveData coroutine builder fun <T> liveData(...): LiveData<T> = CoroutineLiveData(context, timeoutInMs,

    block) internal class CoroutineLiveData<T>( ... ) : MediatorLiveData<T>() { init { ... val scope = CoroutineScope( Dispatchers.Main.immediate + context + supervisorJob) ... } }
  21. liveData coroutine builder class MyViewModel : ViewModel() { val result

    = liveData(Dispatchers.IO) { emit(doComputation()) } }
  22. liveData coroutine builder class MyViewModel : ViewModel() { val result

    = liveData { emit(doComputation()) emitSource(repo.fetchSomething()) } } class MyRepository { fun fetchSomething(): LiveData<String> { ...
  23. liveData coroutine builder class MyViewModel : ViewModel() { private val

    itemId = MutableLiveData<Long>() val item = itemId.switchMap { liveData { emit(fetchItem(it)) } } }
  24. class MyDataSource { private val weather: String get() = listOf("Sunny",

    "Rainy", "Cloudy").shuffled().first() fun fetchWeather(): LiveData<String> = liveData { fun fetchWeatherFlow() = flow { while (true) { delay(2_000) emit(weather) } } } Data Source
  25. emit N values // LiveData -> LiveData val currentWeather: LiveData<String>

    = dataSource.fetchWeather() // flow -> LiveData val currentWeatherFlow: LiveData<String> = liveData { dataSource.fetchWeatherFlow().collect { emit(it) } }
  26. emit N values // LiveData -> LiveData val currentWeather: LiveData<String>

    = dataSource.fetchWeather() // flow -> LiveData val currentWeatherFlow: LiveData<String> = dataSource.fetchWeatherFlow().asLiveData()
  27. emit 1 + emit N // LiveData -> LiveData val

    currentWeather: LiveData<String> = liveData { emit(LOADING_STRING) emitSource(dataSource.fetchWeather()) }
  28. emit 1 + emit N // LiveData -> LiveData val

    currentWeather: LiveData<String> = liveData { emit(LOADING_STRING) emitSource(dataSource.fetchWeather()) } // flow -> LiveData val currentWeatherFlow: LiveData<String> = liveData { emit(LOADING_STRING) emitSource( dataSource.fetchWeatherFlow().asLiveData() ) }
  29. emit 1 + emit N // LiveData -> LiveData val

    currentWeather: LiveData<String> = liveData { emit(LOADING_STRING) emitSource(dataSource.fetchWeather()) } // flow -> LiveData val currentWeatherFlow: LiveData<String> = dataSource.fetchWeatherFlow() .onStart { emit(LOADING_STRING) } .asLiveData()
  30. Transformation // LiveData -> LiveData val currentWeather: LiveData<String> = dataSource.fetchWeather().switchMap

    { liveData { emit(heavyTransformation(it)) } } // flow -> LiveData val currentWeatherFlow: LiveData<String> = dataSource.fetchWeatherFlow() .map { heavyTransformation(it) } .asLiveData()
  31. fun <T> Flow<T>.asLiveData( context: CoroutineContext = EmptyCoroutineContext, timeoutInMs: Long =

    DEFAULT_TIMEOUT ): LiveData<T> = liveData(context, timeoutInMs) { collect { emit(it) } } fun <T> liveData( context: CoroutineContext = EmptyCoroutineContext, timeoutInMs: Long = DEFAULT_TIMEOUT, @BuilderInference block: suspend LiveDataScope<T>.() -> Unit ): LiveData<T> = CoroutineLiveData(context, timeoutInMs, block) cancelling CoroutineLiveData
  32. internal class CoroutineLiveData<T>( ... ) : MediatorLiveData<T>() { init {

    ... blockRunner = BlockRunner( ... ) } override fun onInactive() { super.onInactive() blockRunner?.cancel() } } cancelling CoroutineLiveData
  33. internal class BlockRunner<T>( … ) { fun cancel() { ...

    cancellationJob = scope.launch(Dispatchers.Main.immediate) { delay(timeoutInMs) if (!liveData.hasActiveObservers()) { ... runningJob?.cancel() runningJob = null } } … cancelling CoroutineLiveData
  34. val currentWeather: LiveData<String> = dataSource.fetchWeatherFlow().map { it.toUpperCase() }.onStart { emit("ON

    START") }.asLiveData() }.asLiveData(parentJob) }.asLiveData(viewModelScope.coroutineContext) cancelling CoroutineLiveData
  35. Android Dev Summit 2019 extended Seoul References • LiveData with

    Coroutines and Flow (Android Dev Summit '19) https://www.youtube.com/watch?v=B8ppnjGPAGE&t=416s • LiveData Overview https://developer.android.com/topic/libraries/architecture/livedata • Use Kotlin coroutines with Architecture components https://developer.android.com/topic/libraries/architecture/coroutines • Easy Coroutines in Android: viewModelScope https://medium.com/androiddevelopers/easy-coroutines-in-android-viewmodelscope-25bffb605471 • Lessons learnt using Coroutines Flow in the Android Dev Summit 2019 app https://medium.com/androiddevelopers/lessons-learnt-using-coroutines-flow-4a6b285c0d06 • iosched adssched2019 branch : https://github.com/google/iosched/tree/adssched2019
  36. Android Dev Summit 2019 extended Seoul References • CodeLab :

    Advanced Coroutines with Kotlin Flow and LiveData https://codelabs.developers.google.com/codelabs/advanced-kotlin-coroutines/index.html?index=.. %2F..index#0 • Testing Coroutines on Android (Android Dev Summit '19) https://www.youtube.com/watch?v=KMb0Fs8rCRs&t=11s • Cold flows, hot channels https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9 • Simple design of Kotlin Flow https://medium.com/@elizarov/simple-design-of-kotlin-flow-4725e7398c4c • Kotlin Flows and Coroutines https://medium.com/@elizarov/kotlin-flows-and-coroutines-256260fb3bdb