Slide 1

Slide 1 text

Look into Kotlin Flow > RxJava Taehwan ۨހ౟ܻ উ٘۽੉٘ ѐߊ੗ GDG Korea Android ਍৔૓

Slide 2

Slide 2 text

ٛӝ ੹ী • ੷ח উ٘۽੉٘ ѐߊ੗ੑפ׮. • RxJava৬ Flowী ؀೧ рױೠ ࢸݺਸ ನೣ೤פ׮. • Streamਸ যڌѱ ׮ܙ૑ ঌইࠇפ׮.(ղਊ੉ ખ য۰਎ ࣻ ੓਺) • ৈӝী աয়ח ௏٘ח ݽف UnitTestীࢲ ੘ࢿೠ ௏٘ੑפ׮.

Slide 3

Slide 3 text

RxJava vs Flow

Slide 4

Slide 4 text

RxJava vs Flow RxJava Flow Cold Observable, Single … fl ow, fl owOf Hot PublishSubject, BehaviroSubject … SharedFlow, StateFlow

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Callback ୊ܻ

Slide 7

Slide 7 text

Callback ୊ܻ ఃਕ٘ Ѩ࢝ਸ ਤೠ ч
 Stream Stream ߡౡ ੉߮౟ী ੄ೠ ੑ۱ ч ୊ܻ (Ѩ࢝җ э਷ ৘) ߡౡ Event Callback

Slide 8

Slide 8 text

Callback ୊ܻ @Test fun callbackTestRxJavaNewStream() { var event: ViewEvent? = null Observable.just(“Ѩ࢝ ఃਕ٘") .switchMap { Observable.create { 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 { 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 ઙܐ

Slide 9

Slide 9 text

Callback ୊ܻ @Test fun callbackTestRxJavaNewStream() { var event: ViewEvent? = null Observable.just(“Ѩ࢝ ఃਕ٘") .switchMap { Observable.create { 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 { 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ഥ ഐ୹

Slide 10

Slide 10 text

1ѐ ੉࢚੄ stream ഝਊ ߑߨ

Slide 11

Slide 11 text

1ѐ ੉࢚੄ stream ഝਊ ߑߨ Stream - 1 Stream Stream data Stream - 2 combine Pair

Slide 12

Slide 12 text

2ѐ੄ stream ୊ܻೞӝ val userNameSubject = BehaviorSubject.create() val passwordSubject = BehaviorSubject.create() Observable.combineLatest( userNameSubject, passwordSubject, { name, password -> name to password }) .subscribe { println("Pair $it") } val flowUserName = MutableSharedFlow() val flowPassword = MutableSharedFlow() flowUserName.combine(flowPassword) { name, password -> name to password } .onEach { println("Pair $it") } .launchIn(this) 2ѐ झ౟ܿ ࢤࢿ Combine Pair۽ чਸ ੹׳

Slide 13

Slide 13 text

nameҗ password৬ ߡౡ ੉߮౟ ഝਊ ࢜۽਍ झ౟ܿ ഝਊ

Slide 14

Slide 14 text

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੄ ߡౡ ੉߮౟ ߊࢤ

Slide 15

Slide 15 text

nameҗ password৬ ߡౡ ੉߮౟ ഝਊ ࢜۽਍ झ౟ܿ ഝਊ val userNameSubject = BehaviorSubject.create() val passwordSubject = BehaviorSubject.create() var event: ViewEvent? = null val viewEventSubject = Observable.create { 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() val flowPassword = MutableSharedFlow() 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

Slide 16

Slide 16 text

val userNameSubject = BehaviorSubject.create() val passwordSubject = BehaviorSubject.create() var event: ViewEvent? = null val viewEventSubject = Observable.create { 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() val flowPassword = MutableSharedFlow() 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 { 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

Slide 17

Slide 17 text

val userNameSubject = BehaviorSubject.create() val passwordSubject = BehaviorSubject.create() var event: ViewEvent? = null val viewEventSubject = Observable.create { 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() val flowPassword = MutableSharedFlow() 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

Slide 18

Slide 18 text

val userNameSubject = BehaviorSubject.create() val passwordSubject = BehaviorSubject.create() var event: ViewEvent? = null val viewEventSubject = Observable.create { 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() val flowPassword = MutableSharedFlow() 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 { 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 ߊࢤ

Slide 19

Slide 19 text

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੄ ߡౡ ੉߮౟ ߊࢤ

Slide 20

Slide 20 text

streamਵ۽ List ୊ܻೞӝ

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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() }

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

৵ fl owח stream੉ ইקө? /** * Collects given flow into a [destination] */ public suspend fun Flow.toList( destination: MutableList = ArrayList() ): List = toCollection(destination)

Slide 25

Slide 25 text

Flowب streamਵ۽ ୊ܻ೧ࠁ੗ List to stream Stream Convert item(map) custom ୊ܻ

Slide 26

Slide 26 text

@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 ܻఢ

Slide 27

Slide 27 text

@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 Flow.toListFlow( destination: MutableList = ArrayList() ): Flow> = flow { collect { value -> destination.add(value) } emit(destination) } 2. fl atMapLatest ਵ۽ ߸҃ 1. Collect ୊ܻ 2. collect ٜযৡ item ܻझ౟ ୶о 3. emitਵ۽ list ؍૗

Slide 28

Slide 28 text

@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) data class NewSection(val title: String, val list: List)

Slide 29

Slide 29 text

Rx Subject vs SharedFlow vs StateFlow

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

SharedFlow vs StateFlow SharedFlow StateFlow ੿੄ MutableSharedFlow() MutableStateFlow() ӝࠄч ৈࠗ X O Replay ઑѤ ӝࠄ਷ ೞ૑ ঋ਺ ࢸ੿ী ٮۄ ׮ܴ ೦࢚ replay Bu ff er ࢎ੉ૉ ӝࠄч 0 ӝࠄч਷ ղࠗ ࢸ੿ Bu ff er ২࣌ SUSPEND DROP_OLDEST

Slide 32

Slide 32 text

SharedFlow

Slide 33

Slide 33 text

SharedFlow @Test fun sharedFlow() = runBlocking { val sharedFlow = MutableSharedFlow() sharedFlow.onEach { delay(500.toLong()) // য়ې Ѧܻח ੘স println(“New value $it") } .launchIn(this) sharedFlow.emit(true) sharedFlow.emit(false) } @Test fun sharedFlow() = runBlocking { val sharedFlow = MutableSharedFlow( extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST ) sharedFlow.onEach { delay(500.toLong()) // য়ې Ѧܻח ੘স println(“New value : $it") } .launchIn(this) sharedFlow.emit(true) sharedFlow.emit(false) }

Slide 34

Slide 34 text

SharedFlow Emit onEach - delay(0.5sec) tryEmit onEach - delay(0.5sec) োࣘਵ۽ ٜযয়ݶ drop onEachীࢲ delay()ী ੄೧ drop MutableSharedFlow - default MutableSharedFlow - drop_oldest - capacity 1

Slide 35

Slide 35 text

SharedFlow @Test fun sharedFlow() = runBlocking { val sharedFlow = MutableSharedFlow() sharedFlow.onEach { delay(500.toLong()) // য়ې Ѧܻח ੘স println(“New value $it") } .launchIn(this) sharedFlow.emit(true) sharedFlow.emit(false) } @Test fun sharedFlow() = runBlocking { val sharedFlow = MutableSharedFlow( 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 ୐ ߣ૩ чਸ ୊ܻ

Slide 36

Slide 36 text

StateFlow

Slide 37

Slide 37 text

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() }

Slide 38

Slide 38 text

StateFlow زੌч ੹࣠ onEach Default ࢜۽਍ ч ੹࣠ onEach Default

Slide 39

Slide 39 text

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ഥ ୹۱

Slide 40

Slide 40 text

@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 ૑੿

Slide 41

Slide 41 text

@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

Slide 42

Slide 42 text

@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

Slide 43

Slide 43 text

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? = 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 // ࢤۚ } }

Slide 44

Slide 44 text

хࢎ೤פ׮.

Slide 45

Slide 45 text

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