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

2021-Kotlin-night

TaeHwan
November 17, 2021

 2021-Kotlin-night

TaeHwan

November 17, 2021
Tweet

More Decks by TaeHwan

Other Decks in Programming

Transcript

  1. Look into Kotlin Flow > RxJava Taehwan ۨހ౟ܻ উ٘۽੉٘ ѐߊ੗

    GDG Korea Android ਍৔૓
  2. ٛӝ ੹ী • ੷ח উ٘۽੉٘ ѐߊ੗ੑפ׮. • RxJava৬ Flowী ؀೧

    рױೠ ࢸݺਸ ನೣ೤פ׮. • Streamਸ যڌѱ ׮ܙ૑ ঌইࠇפ׮.(ղਊ੉ ખ য۰਎ ࣻ ੓਺) • ৈӝী աয়ח ௏٘ח ݽف UnitTestীࢲ ੘ࢿೠ ௏٘ੑפ׮.
  3. RxJava vs Flow

  4. RxJava vs Flow RxJava Flow Cold Observable, Single … fl

    ow, fl owOf Hot PublishSubject, BehaviroSubject … SharedFlow, StateFlow
  5. RxJava vs Flow RxJava Flow ੿੄ Observable, Single … fl

    ow, fl owOf, MutableSharedFlow … ੹୓ झா઴۞ subscribeOn Coroutine Scope ੿੄ী ٮܴ झா઴۞ ߸҃ observeOn / ੉റ ߸҃ fl owOn / ੉੹ ߸҃ ҳة subscribe collect {} or launchIn ઙ೤ ݆਷ Operator ઁҕਵ۽ ೟ण য۰਑ ੸਷ Operator ઁҕਵ۽ ೟ण ए਑
  6. Callback ୊ܻ

  7. Callback ୊ܻ ఃਕ٘ Ѩ࢝ਸ ਤೠ ч
 Stream Stream ߡౡ ੉߮౟ী

    ੄ೠ ੑ۱ ч ୊ܻ (Ѩ࢝җ э਷ ৘) ߡౡ Event Callback
  8. Callback ୊ܻ @Test fun callbackTestRxJavaNewStream() { var event: ViewEvent? =

    null Observable.just(“Ѩ࢝ ఃਕ٘") .switchMap { Observable.create<Boolean> { emitter -> event = ViewEvent { emitter.onNext(true) } emitter.setCancellable { event = null } } } .subscribe { println("Ѩ࢝ೞӝ") } event?.event() } @Test fun callbackTestFlowNewStream() = runBlockingTest { var event: ViewEvent? = null flowOf(“Ѩ࢝ ఃਕ٘") .flatMapLatest { callbackFlow { event = ViewEvent { trySend(true) } awaitClose { event = null } } } .onEach { println("Ѩ࢝ೞӝ") } .launchIn(this) event?.event() } @Test fun callbackTestRxJavaNewStream() { Observable.just(“Ѩ࢝ ఃਕ٘") .switchMap { keyword -> Observable.create<Boolean> { emitter -> event = ViewEvent { emitter.onNext(keyword) } emitter.setCancellable { event = null } } } event?.event() } @Test fun callbackTestFlowNewStream() = runBlockingTest { flowOf(“Ѩ࢝ ఃਕ٘") .flatMapLatest { keyword -> callbackFlow { event = ViewEvent { trySend(keyword) } awaitClose { event = null } } } event?.event() } ఃਕ٘ Callback ١۾ Callback ઙܐ
  9. Callback ୊ܻ @Test fun callbackTestRxJavaNewStream() { var event: ViewEvent? =

    null Observable.just(“Ѩ࢝ ఃਕ٘") .switchMap { Observable.create<Boolean> { emitter -> event = ViewEvent { emitter.onNext(true) } emitter.setCancellable { event = null } } } .subscribe { println("Ѩ࢝ೞӝ") } event?.event() } @Test fun callbackTestFlowNewStream() = runBlockingTest { var event: ViewEvent? = null flowOf(“Ѩ࢝ ఃਕ٘") .flatMapLatest { callbackFlow { event = ViewEvent { trySend(true) } awaitClose { event = null } } } .onEach { println("Ѩ࢝ೞӝ") } .launchIn(this) event?.event() } @Test fun callbackTestRxJavaNewStream() { Observable.just(“Ѩ࢝ ఃਕ٘") .switchMap { Observable.create<Boolean> { emitter -> event = ViewEvent { emitter.onNext(true) } emitter.setCancellable { event = null } } } event?.event() } @Test fun callbackTestFlowNewStream() = runBlockingTest { flowOf(“Ѩ࢝ ఃਕ٘") .flatMapLatest { callbackFlow { event = ViewEvent { trySend(true) } awaitClose { event = null } } } event?.event() } Event ߊࢤ 1ഥ ࢤࢿ nഥ ഐ୹ nߣ प೯ 1ഥ ഐ୹
  10. 1ѐ ੉࢚੄ stream ഝਊ ߑߨ

  11. 1ѐ ੉࢚੄ stream ഝਊ ߑߨ Stream - 1 Stream Stream

    data Stream - 2 combine Pair
  12. 2ѐ੄ stream ୊ܻೞӝ val userNameSubject = BehaviorSubject.create<String>() val passwordSubject =

    BehaviorSubject.create<String>() Observable.combineLatest( userNameSubject, passwordSubject, { name, password -> name to password }) .subscribe { println("Pair $it") } val flowUserName = MutableSharedFlow<String>() val flowPassword = MutableSharedFlow<String>() flowUserName.combine(flowPassword) { name, password -> name to password } .onEach { println("Pair $it") } .launchIn(this) 2ѐ झ౟ܿ ࢤࢿ Combine Pair۽ чਸ ੹׳
  13. nameҗ password৬ ߡౡ ੉߮౟ ഝਊ ࢜۽਍ झ౟ܿ ഝਊ

  14. nameҗ password৬ ߡౡ ੉߮౟ ഝਊ ࢜۽਍ झ౟ܿ ഝਊ Stream-1 Stream

    Login ୊ܻ Stream-2 Combine - Stream-1җ Stream-2ীࢲ nameҗ passwordܳ ੑ۱ - Stream-3ীࢲ ߡౡ ੉߮౟ ߊࢤ - Name, passowrd ч੉ ੑ۱غ঻׮ݶ ঱ઁٚ ߡౡ ੉߮౟ח ߊࢤ ೡ ࣻ ੓׮. Id ੑ۱ Password ੑ۱ Id, password ੑ۱ غ঻׮ݶ stream 3੄ ߡౡ ੉߮౟ ߊࢤ
  15. nameҗ password৬ ߡౡ ੉߮౟ ഝਊ ࢜۽਍ झ౟ܿ ഝਊ val userNameSubject

    = BehaviorSubject.create<String>() val passwordSubject = BehaviorSubject.create<String>() var event: ViewEvent? = null val viewEventSubject = Observable.create<Boolean> { emitter -> event = ViewEvent { emitter.onNext(true) } emitter.setCancellable { event = null } } Observable.combineLatest( userNameSubject, passwordSubject, { name, password -> name to password }) .switchMap { pair -> viewEventSubject.map { pair } } .subscribe { println("Pair $it") } val flowUserName = MutableSharedFlow<String>() val flowPassword = MutableSharedFlow<String>() var event: EventTest? = null val flowViewEvent = callbackFlow { event = EventTest { trySend(true) } awaitClose { event = null } } flowUserName .combine(flowPassword) { name, password -> name to password } .flatMapLatest { pair -> flowViewEvent.map { pair } } .onEach { println("Pair $it") } .launchIn(this) Id, password pair Callback event Button Event
  16. val userNameSubject = BehaviorSubject.create<String>() val passwordSubject = BehaviorSubject.create<String>() var event:

    ViewEvent? = null val viewEventSubject = Observable.create<Boolean> { emitter -> event = ViewEvent { emitter.onNext(true) } emitter.setCancellable { event = null } } Observable.combineLatest( userNameSubject, passwordSubject, { name, password -> name to password }) .switchMap { pair -> viewEventSubject.map { pair } } .subscribe { println("Pair $it") } val flowUserName = MutableSharedFlow<String>() val flowPassword = MutableSharedFlow<String>() var event: EventTest? = null val flowViewEvent = callbackFlow { event = EventTest { trySend(true) } awaitClose { event = null } } flowUserName .combine(flowPassword) { name, password -> name to password } .flatMapLatest { pair -> flowViewEvent.map { pair } } .onEach { println("Pair $it") } .launchIn(this) var event: ViewEvent? = null val viewEventSubject = Observable.create<Boolean> { emitter -> event = ViewEvent { emitter.onNext(true) } emitter.setCancellable { event = null } } var event: EventTest? = null val flowViewEvent = callbackFlow { event = EventTest { trySend(true) } awaitClose { event = null } } nameҗ password৬ ߡౡ ੉߮౟ ഝਊ ࢜۽਍ झ౟ܿ ഝਊ Callback event
  17. val userNameSubject = BehaviorSubject.create<String>() val passwordSubject = BehaviorSubject.create<String>() var event:

    ViewEvent? = null val viewEventSubject = Observable.create<Boolean> { emitter -> event = ViewEvent { emitter.onNext(true) } emitter.setCancellable { event = null } } Observable.combineLatest( userNameSubject, passwordSubject, { name, password -> name to password }) .switchMap { pair -> viewEventSubject.map { pair } } .subscribe { println("Pair $it") } val flowUserName = MutableSharedFlow<String>() val flowPassword = MutableSharedFlow<String>() var event: EventTest? = null val flowViewEvent = callbackFlow { event = EventTest { trySend(true) } awaitClose { event = null } } flowUserName .combine(flowPassword) { name, password -> name to password } .flatMapLatest { pair -> flowViewEvent.map { pair } } .onEach { println("Pair $it") } .launchIn(this) Observable.combineLatest( userNameSubject, passwordSubject, { name, password -> name to password }) flowUserName .combine(flowPassword) { name, password -> name to password } nameҗ password৬ ߡౡ ੉߮౟ ഝਊ ࢜۽਍ झ౟ܿ ഝਊ Combine
  18. val userNameSubject = BehaviorSubject.create<String>() val passwordSubject = BehaviorSubject.create<String>() var event:

    ViewEvent? = null val viewEventSubject = Observable.create<Boolean> { emitter -> event = ViewEvent { emitter.onNext(true) } emitter.setCancellable { event = null } } Observable.combineLatest( userNameSubject, passwordSubject, { name, password -> name to password }) .switchMap { pair -> viewEventSubject.map { pair } } .subscribe { println("Pair $it") } val flowUserName = MutableSharedFlow<String>() val flowPassword = MutableSharedFlow<String>() var event: EventTest? = null val flowViewEvent = callbackFlow { event = EventTest { trySend(true) } awaitClose { event = null } } flowUserName .combine(flowPassword) { name, password -> name to password } .flatMapLatest { pair -> flowViewEvent.map { pair } } .onEach { println("Pair $it") } .launchIn(this) val viewEventSubject = Observable.create<Boolean> { emitter -> event = ViewEvent { emitter.onNext(true) } } .switchMap { pair -> viewEventSubject.map { pair } } val flowViewEvent = callbackFlow { event = EventTest { trySend(true) } } .flatMapLatest { pair -> flowViewEvent.map { pair } } nameҗ password৬ ߡౡ ੉߮౟ ഝਊ ࢜۽਍ झ౟ܿ ഝਊ Button event റ pair ч ܻఢ Button event ߊࢤ
  19. nameҗ password৬ ߡౡ ੉߮౟ ഝਊ ࢜۽਍ झ౟ܿ ഝਊ Stream-1 Stream

    Login ୊ܻ Stream-2 Combine - Stream-1җ Stream-2ীࢲ nameҗ passwordܳ ੑ۱ - Stream-3ীࢲ ߡౡ ੉߮౟ ߊࢤ - Name, passowrd ч੉ ੑ۱غ঻׮ݶ ঱ઁٚ ߡౡ ੉߮౟ח ߊࢤ ೡ ࣻ ੓׮. Id ੑ۱ Password ੑ۱ Id, password ੑ۱ غ঻׮ݶ stream 3੄ ߡౡ ੉߮౟ ߊࢤ
  20. streamਵ۽ List ୊ܻೞӝ

  21. streamਵ۽ List ୊ܻೞӝ List to stream Stream Convert item(map) toList()

  22. streamਵ۽ List ୊ܻೞӝ @Test fun testRxJavaConvertList() { val items =

    listOf(1, 2, 3, 4, 5) Observable.fromIterable(items) .map { "Item $it" } .toList() .subscribe { t1, _ -> println("t1 $t1") } } @Test fun testFlowConvertList() = runBlockingTest { listOf(1, 2, 3, 4, 5).asFlow() .cancellable() .map { "Item $it" } .toList() }
  23. Listܳ оҕ റ List stream ୊ܻ @Test fun testRxJavaConvertList() {

    val items = listOf(1, 2, 3, 4, 5) Observable.fromIterable(items) .map { "Item $it" } .toList() .subscribe { t1, _ -> println("t1 $t1") } } @Test fun testFlowConvertList() = runBlockingTest { listOf(1, 2, 3, 4, 5).asFlow() .cancellable() .map { "Item $it" } .toList() } fl ow - Stream X RxJava - Stream O
  24. ৵ fl owח stream੉ ইקө? /** * Collects given flow

    into a [destination] */ public suspend fun <T> Flow<T>.toList( destination: MutableList<T> = ArrayList() ): List<T> = toCollection(destination)
  25. Flowب streamਵ۽ ୊ܻ೧ࠁ੗ List to stream Stream Convert item(map) custom

    ୊ܻ
  26. @Test fun testFlowConvertListSolutionOne() = runBlockingTest { val list = listOf(1,

    2, 3, 4, 5) flowOf(list) .map { val newList = it.asFlow().cancellable() .map { "Item $it" } .toList() newList } .onEach { println("items $it") } .launchIn(this) } @Test fun testFlowConvertListSolutionOne() = runBlockingTest { .map { val newList = it.asFlow().cancellable() .map { "Item $it" } .toList() newList } } Flowب streamਵ۽ ୊ܻ೧ࠁ੗ - ೧Ѿ ߑߨ 1- ׀ী উࠁ੉ח newList ܻఢ
  27. @Test fun testFlowConvertListSolutionOne() = runBlockingTest { .flatMapLatest { it.asFlow().cancellable() .map

    { "Item $it" } .toListFlow() } } Flow - streamਵ۽ ੉যоӝ - ೧Ѿ ߑߨ 2 : Custom ೣࣻ- @Test fun testFlowConvertListSolutionOne() = runBlockingTest { val list = listOf(1, 2, 3, 4, 5) flowOf(list) .flatMapLatest { it.asFlow().cancellable() .map { "Item $it" } .toListFlow() } .onEach { println("items $it") } .launchIn(this) } 1. Custom ೣࣻ۽ ߸҃ suspend fun <T> Flow<T>.toListFlow( destination: MutableList<T> = ArrayList() ): Flow<List<T>> = flow { collect { value -> destination.add(value) } emit(destination) } 2. fl atMapLatest ਵ۽ ߸҃ 1. Collect ୊ܻ 2. collect ٜযৡ item ܻझ౟ ୶о 3. emitਵ۽ list ؍૗
  28. @Test fun testFlowSection() = runBlockingTest { listOf(Section("Title", (1..5).toList())).asFlow() .cancellable() .flatMapLatest

    { section -> section.list.asFlow().cancellable() .map { "Item $it" } .toListFlow() .map { NewSection(section.title, it) } .toListFlow() } .onEach { println("New list $it") } .launchIn(this) } @Test fun testRxJavaSection() { val items = listOf(Section("Title", (1..5).toList())) Observable.fromIterable(items) .switchMapSingle { section -> Observable.fromIterable(section.list) .map { "Item $it" } .toList() .map { NewSection(section.title, it) } .toList() } .subscribe { t1, _ -> println("t1 $t1") } } @Test fun testRxJavaSection() { val items = listOf(Section("Title", (1..5).toList())) .switchMapSingle { section -> Observable.fromIterable(section.list) .toList() .map { NewSection(section.title, it) } .toList() } } @Test fun testFlowSection() = runBlockingTest { .flatMapLatest { section -> section.list.asFlow().cancellable() .toListFlow() .map { NewSection(section.title, it) } .toListFlow() } } Flow - streamਵ۽ ੉যоӝ data class Section(val title: String, val list: List<Int>) data class NewSection(val title: String, val list: List<String>)
  29. Rx Subject vs SharedFlow vs StateFlow

  30. Rx Subject vs SharedFlow vs StateFlow Rx Subject SharedFlow StateFlow

    ز੘ ߑध Hot ч੄ ୊ܻ ૑ࣘ੸ੋ ч੄ ୊ܻ оמ onNext() ӝࠄ ࢸ੿ : ݃૑݄ чਸ ୊ܻೡ ࣻ ੓׮. ࢸ੿ী ٮۄ খ/ٍ੄ ؘ੉ఠܳ ߡܾ ࣻ ੓׮. ೦࢚ ୭न ч݅ਸ о૓׮. Value ߸ࣻ
  31. SharedFlow vs StateFlow SharedFlow StateFlow ੿੄ MutableSharedFlow() MutableStateFlow() ӝࠄч ৈࠗ

    X O Replay ઑѤ ӝࠄ਷ ೞ૑ ঋ਺ ࢸ੿ী ٮۄ ׮ܴ ೦࢚ replay Bu ff er ࢎ੉ૉ ӝࠄч 0 ӝࠄч਷ ղࠗ ࢸ੿ Bu ff er ২࣌ SUSPEND DROP_OLDEST
  32. SharedFlow

  33. SharedFlow @Test fun sharedFlow() = runBlocking { val sharedFlow =

    MutableSharedFlow<Boolean>() sharedFlow.onEach { delay(500.toLong()) // য়ې Ѧܻח ੘স println(“New value $it") } .launchIn(this) sharedFlow.emit(true) sharedFlow.emit(false) } @Test fun sharedFlow() = runBlocking { val sharedFlow = MutableSharedFlow<Boolean>( extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST ) sharedFlow.onEach { delay(500.toLong()) // য়ې Ѧܻח ੘স println(“New value : $it") } .launchIn(this) sharedFlow.emit(true) sharedFlow.emit(false) }
  34. SharedFlow Emit onEach - delay(0.5sec) tryEmit onEach - delay(0.5sec) োࣘਵ۽

    ٜযয়ݶ drop onEachীࢲ delay()ী ੄೧ drop MutableSharedFlow - default MutableSharedFlow - drop_oldest - capacity 1
  35. SharedFlow @Test fun sharedFlow() = runBlocking { val sharedFlow =

    MutableSharedFlow<Boolean>() sharedFlow.onEach { delay(500.toLong()) // য়ې Ѧܻח ੘স println(“New value $it") } .launchIn(this) sharedFlow.emit(true) sharedFlow.emit(false) } @Test fun sharedFlow() = runBlocking { val sharedFlow = MutableSharedFlow<Boolean>( extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST ) sharedFlow.onEach { delay(500.toLong()) // য়ې Ѧܻח ੘স println(“New value : $it") } .launchIn(this) sharedFlow.emit(true) sharedFlow.emit(false) } 0.5ୡ റ true ୹۱
 0.5ୡ റ false ୹۱ 0.5ୡ റ true ୹۱
 ੘স੉ ՘դ റ queue ୐ ߣ૩ чਸ ୊ܻ
  36. StateFlow

  37. StateFlow @Test fun stateFlow() = runBlockingTest { val stateFlow =

    MutableStateFlow("Default") val job = stateFlow.onEach { println("out message $it") } .launchIn(this) stateFlow.tryEmit("Default") stateFlow.tryEmit("New message") job.cancel() }
  38. StateFlow زੌч ੹࣠ onEach Default ࢜۽਍ ч ੹࣠ onEach Default

  39. StateFlow @Test fun stateFlow() = runBlockingTest { val stateFlow =

    MutableStateFlow("Default") val job = stateFlow.onEach { println("out message $it") } .launchIn(this) stateFlow.tryEmit("Default") stateFlow.tryEmit("New message") job.cancel() } Default 1ഥ ୹۱ New Message 1ഥ ୹۱
  40. @Test fun stateFlow() = runBlockingTest { val stateFlow = MutableStateFlow("Default")

    val job = stateFlow.onEach { println("out message $it") } .launchIn(this) stateFlow.tryEmit("Default") stateFlow.tryEmit("New message") job.cancel() } @Test fun stateFlow() = runBlockingTest { val stateFlow = MutableStateFlow("Default") } StateFlow StateFlow ӝࠄчਵ۽ Default ૑੿
  41. @Test fun stateFlow() = runBlockingTest { val stateFlow = MutableStateFlow("Default")

    val job = stateFlow.onEach { println("out message $it") } .launchIn(this) stateFlow.tryEmit("Default") stateFlow.tryEmit("New message") job.cancel() } @Test fun stateFlow() = runBlockingTest { stateFlow.tryEmit("Default") } StateFlow زੌ ч emit
  42. @Test fun stateFlow() = runBlockingTest { val stateFlow = MutableStateFlow("Default")

    val job = stateFlow.onEach { println("out message $it") } .launchIn(this) stateFlow.tryEmit("Default") stateFlow.tryEmit("New message") job.cancel() } @Test fun stateFlow() = runBlockingTest { stateFlow.tryEmit("New message") } StateFlow ࢜۽਍ ч emit
  43. StateFlow public override var value: T get() = NULL.unbox(_state.value) set(value)

    { updateState(null, value ?: NULL) } private fun updateState(expectedState: Any?, newState: Any): Boolean { var curSequence = 0 var curSlots: Array<StateFlowSlot?>? = this.slots // benign race, we will not use it synchronized(this) { val oldState = _state.value if (expectedState != null && oldState != expectedState) return false if (oldState == newState) return true // ࢤۚ } }
  44. хࢎ೤פ׮.

  45. ਋ܻ ই੉ Әਲ਼੄ द੘, ۨހ౟ܻ ੗֗੄ ਊت ҙܻ, Әਲ਼ Үਭ,

    ઱ध ై੗ө૑ ೠ ߣী! ੗֗ ਊت ݒפ੷, ۨހ౟ܻ۽ ਋ܻ о઒ Әਲ਼ਸ द੘ೞࣁਃ.