Slide 1

Slide 1 text

Kotlin Coroutines Taehwan

Slide 2

Slide 2 text

ࣗѐ • GDG Seoul ਍৔૓ • ٘۽੉٘ ա੉எ ਍৔૓ • RGP Korea • ਃӝਃ উ٘۽੉٘ ѐߊ • Blog : Է ݆਷ ѐߊ੗о غ੗! (https://thdev.tech)

Slide 3

Slide 3 text

ೣࣻ ਋ܻо ঌҊ ੓ח

Slide 4

Slide 4 text

਋ܻо ঌҊ੓ח ೣࣻ fun calc() fun MutableList.sum() (0..10).toMutableList().sum() return 55 +0
 +1
 +2
 . . . +10

Slide 5

Slide 5 text

਋ܻо ঌҊ੓ח ೣࣻ • ഐ୹ೠ Ҕਵ۽ جইয়ӝ ਤ೧ࢲח return੉ ೙ਃೞ׮ • return ੉੹ীח ݫੋ ೣࣻ੄ ׮਺ ۄੋਸ प೯ೞ૑ ঋח׮ • ੉۠ ੌ߈੸ੋ ೣࣻܳ Subroutine੉ۄ ೠ׮.

Slide 6

Slide 6 text

Subroutine In computer programming, a subroutine is a sequence of program instructions that performs a specific task, packaged as a unit In different programming languages, a subroutine may be called a procedure, a function, a routine, a method, or a subprogram. The generic term callable unit is sometimes used. ୹୊ : https://en.wikipedia.org/wiki/Subroutine

Slide 7

Slide 7 text

Coroutines

Slide 8

Slide 8 text

Coroutines According to Donald Knuth, Melvin Conway coined the term coroutine in 1958 when he applied it to construction of an assembly program. The first published explanation of the coroutine appeared later, in 1963 ୹୊ : https://en.wikipedia.org/wiki/Coroutine

Slide 9

Slide 9 text

Coroutines Coroutines are computer-program components that generalize subroutines for non-preemptive multitasking, by allowing multiple entry points for suspending and resuming execution at certain locations. Coroutines are well-suited for implementing familiar program components such as cooperative tasks, exceptions, event loops, iterators, infinite lists and pipes. ୹୊ : https://en.wikipedia.org/wiki/Coroutine

Slide 10

Slide 10 text

Coroutines Entry point ৈ۞ ѐ ೲਊೞח subroutine ঱ઁٚ ੌद ੿૑, प೯ оמ

Slide 11

Slide 11 text

Coroutines fun calc() fun MutableList.sum() Loop fun MutableList.sum() Loop fun MutableList.sum() Loop fun MutableList.sum() Loop (0..10).toMutableList().sum() . . .

Slide 12

Slide 12 text

Coroutines Thread?

Slide 13

Slide 13 text

Coroutines Thread? One can think of a coroutine as a light-weight thread. The biggest difference is that coroutines are very cheap, almost free: we can create thousands of them, and pay very little in terms of performance. Light-weight thread.

Slide 14

Slide 14 text

Coroutines private suspend fun MutableList.sum(): Int = this.sumBy { it } @Test fun test() { println("run") CoroutineScope(Dispatchers.Default).launch { (0..10).forEach { val sum = (it..10).toMutableList().sum() println(sum) } } println("wait") runBlocking { delay(500L) } println("Test end") } प೯ Ѿҗ run wait 55 55 54 . . . 27 19 10 Test end

Slide 15

Slide 15 text

Coroutines private suspend fun MutableList.sum(): Int = this.sumBy { it } @Test fun test() { println("run") CoroutineScope(Dispatchers.Default).launch { (0..10).forEach { val sum = (it..10).toMutableList().sum() println(sum) } } println("wait") runBlocking { delay(500L) } println("Test end") } 1. 500 ms ؀ӝ 3. 500 ms ؀ӝ റ Test end 2. 500 ms زউ 0..10ਸ ؊ೞח sum प೯

Slide 16

Slide 16 text

Kotlin Coroutines

Slide 17

Slide 17 text

Kotlin Coroutines Kotlin 1.3.10/Coroutines 1.0.1 ੉ਊ ௏ܖ౯ ഝਊ оמ Asynchronous or non-blocking programming ઁҕ ׮নೠ platform ઁҕ Server-side Desktop Mobile Application

Slide 18

Slide 18 text

Kotlin Coroutines dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.1' } buildscript { ext.kotlin_version = ‘1.3.10' } repository { jcenter() } Android implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'

Slide 19

Slide 19 text

Kotlin Coroutines private suspend fun MutableList.sum(): Int = this.sumBy { it } @Test fun test() { println("run") CoroutineScope(Dispatchers.Default).launch { (0..10).forEach { val sum = (it..10).toMutableList().sum() println(sum) } } println("wait") runBlocking { delay(500L) } println("Test end") } CoroutineScope runBlocking suspend Dispatchers Launch Delay

Slide 20

Slide 20 text

Kotlin Coroutines ੉ ௏٘ীࢲ ঌইঠ ೡ ղਊ CoroutineScope GlobalScope Coroutines ز੘ Dispatchers suspend

Slide 21

Slide 21 text

CoroutineScope

Slide 22

Slide 22 text

CoroutineScope public interface CoroutineScope { /** * Context of this scope. */ public val coroutineContext: CoroutineContext } internal class ContextScope(context: CoroutineContext) : CoroutineScope { override val coroutineContext: CoroutineContext = context } @Suppress("FunctionName") public fun CoroutineScope(context: CoroutineContext): CoroutineScope = ContextScope(if (context[Job] != null) context else context + Job())

Slide 23

Slide 23 text

CoroutineScope ௏ܖ౯ ੿੄ܳ ਤೠ Scope ઁҕ CoroutineContext ഋకܳ ૑੿ Main, Default, IO … launch, async ١ਸ ా೧ scope प೯ ഋక ੿੄

Slide 24

Slide 24 text

CoroutineScope CoroutineScope ഝਊ द ࢤݺ ઱ӝܳ ٮܰب۾ ೠ׮ Androidীࢲח Activity lifecycle ژח fragment lifecycleਸ ٮܲ׮.

Slide 25

Slide 25 text

CoroutineScope Activity Lifecycle class SampleActivity : AppCompatActivity(), CoroutineScope { private val job = Job() override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job override fun onDestroy() { super.onDestroy() job.cancel() } } ز੘ ઺ੋ CoroutineScope੄ Jobਸ ઺૑ೞب۾ ਃ୒ೠ׮ ӝࠄ Threadܳ Mainਵ۽ ୊ܻೞب۾ ੿੄ೠ׮

Slide 26

Slide 26 text

CoroutineScope Job CoroutineScope(/* thread type */)ਵ۽ ੿੄ೞݴ, launch, actor ١ ഝਊ launch {}੄ return jobਸ ా೧ ز੘ ૑੿ оמ join() : scope ز੘੉ ՘զ ٸө૑ ؀ӝೞݴ, CoroutinScope উীࢲ ഐ୹ оמ cancel() : ੘স ઺ੋ ز੘ਸ ઙܐ ਬب start() : Scope ࢚కܳ ഛੋೞݴ, ই૒ द੘ೞ૑ ঋওਸ ҃਋ start

Slide 27

Slide 27 text

CoroutineScope Job

Slide 28

Slide 28 text

CoroutineScope Scope ઙܐܳ delay ؀ӝೞח ҃਋ private suspend fun MutableList.sum(): Int { val ret = this.sumBy { it } delay(100) return ret } @Test fun test() { println("run") CoroutineScope(Dispatchers.Default).launch { (0..10).forEach { val sum = (it..10).toMutableList().sum() println(sum) } } println("wait") runBlocking { delay(500L) } println("Test end") } दрਵ۽ח ੿ഛೞ૑ ঋ׮ ੐੄۽ delay ੸ਊ run wait 55 55 54 52 Test end

Slide 29

Slide 29 text

CoroutineScope Scope ઙܐܳ delay ؀ӝೞח ҃਋ private suspend fun MutableList.sum(): Int { val ret = this.sumBy { it } delay(100) return ret } @Test fun test() { println("run") CoroutineScope(Dispatchers.Default).launch { (0..10).forEach { val sum = (it..10).toMutableList().sum() println(sum) } } println("wait") runBlocking { delay(500L) } println("Test end") } दрਵ۽ח ੿ഛೞ૑ ঋ׮ ੐੄۽ delay ੸ਊ run wait 55 55 54 52 Test end Ӓۢ delay ؀न ੘স੉ ՘աӡ ӝ׮۰ࠁ੗.

Slide 30

Slide 30 text

CoroutineScope Scope ઙܐܳ delay ؀ӝೞח ҃਋ private suspend fun MutableList.sum(): Int { val ret = this.sumBy { it } delay(100) return ret } @Test fun test() { println("run") (0..10).forEach { val sum = (it..10).toMutableList().sum() println(sum) } } println("wait") runBlocking { } println("Test end") } run wait 55 55 54 52 Test end CoroutineScope(Dispatchers.Default).launch { delay(500L) job.join() val job =

Slide 31

Slide 31 text

CoroutineScope Scope ઙܐܳ delay ؀ӝೞח ҃਋ private suspend fun MutableList.sum(): Int { val ret = this.sumBy { it } delay(100) return ret } @Test fun test() { println("run") val job = CoroutineScope(Dispatchers.Default).launch { (0..10).forEach { val sum = (it..10).toMutableList().sum() println(sum) } } println("wait") runBlocking { job.join() } println("Test end") } launch੄ returnੋ Jobਸ ഝਊೞݶ ৘ஏ ࠛоמೠ दрਵ۽ ઙܐܳ ӝ׮ܾ ೙ਃо হয૓׮.

Slide 32

Slide 32 text

GlobalScope

Slide 33

Slide 33 text

GlobalScope object GlobalScope : CoroutineScope { /** * Returns [EmptyCoroutineContext]. */ override val coroutineContext: CoroutineContext get() = EmptyCoroutineContext }

Slide 34

Slide 34 text

GlobalScope CoroutineScopeਸ ࢚ࣘ ߉ই ҳഅ೧ك object ҳഅ୓ CoroutineScopeҗ ׮ܰѱ Application/Demon Lifecycleਸ ٮܰب۾ ѐߊ ೙ਃ GlobalScopeਵ۽ द੘ೞݴ launch(/* thread type */), actor ١ਸ ഝਊ

Slide 35

Slide 35 text

GlobalScope ࢎਊ ৘ fun ReceiveChannel.sqrt(): ReceiveChannel = GlobalScope.produce(Dispatchers.Unconfined) { for (number in this@sqrt) { send(Math.sqrt(number.toDouble())) } }

Slide 36

Slide 36 text

Coroutines ز੘

Slide 37

Slide 37 text

Coroutines ز੘ launch CoroutineScope/GlobalScope੄ ௏٘ܳ ੿੄ XxScope.launch(/* Thread type ૑੿ */) ੉޷ Scope উ੉ۄݶ launch(/* Thread type ૑੿*/)ਵ۽ thread type ߸҃ Thread type(Dispatchers) ૑੿ೞ૑ ঋਸ ҃਋ ࢚ਤ scope thread typeਸ ٮܲ׮

Slide 38

Slide 38 text

Coroutines ز੘ async/await async : ௏٘ ࠶ۅਸ ੿੄ await : ௏٘ ࠶ۅ੉ ઙܐೞӡ ӝ׮ܽ׮ async(/* Thread type ૑੿ */) launch৬ زੌೞݴ, ૑੿ೞ૑ ঋਵݶ ࢚ਤ scope thread typeਸ ٮܲ׮ 1ѐ ੉࢚੄ coroutine زӝചо ೙ਃೠ ҃਋ ਬਊೞѱ ࢎਊೡ ࣻ ੓׮

Slide 39

Slide 39 text

Coroutines ز੘ Actor actorী ݫद૑ܳ ੹࣠ೞҊ, ੉ܳ ୊ܻೡ ࣻ ੓׮. actor {} ഋక۽ ؘ੉ఠܳ ࠁմ׮ send(coroutine scope) offer(no coroutine scope)

Slide 40

Slide 40 text

Coroutines ز੘ Channel channel਷ ૑੿ೠ ߡಌ ࢎ੉ૉܳ о૑ח channelਸ ࢤࢿೠ׮ ӝࠄч਷ ߡಌࢎ੉ૉо হ׮

Slide 41

Slide 41 text

Coroutines ز੘ Channel val channel = Channel() launch { // this might be heavy CPU-consuming computation or async logic, we'll just send five squares for (x in 1..5) channel.send(x + x) } // here we print five received integers: repeat(5) { println(channel.receive()) } println("Done!") 2 4 6 8 10 Done!

Slide 42

Slide 42 text

Coroutines ز੘ produce produceܳ ా೧ ReceiveChannelী ؘ੉ఠܳ send ೡ ࣻ ੓׮ ੌ੿दр, ੌ੿ ੉߮౟ܳ ReceiveChannel۽ ੹࣠ೡ ࣻ ੓׮

Slide 43

Slide 43 text

Coroutines ز੘ produce private suspend fun selectFizzBuzz(fizz: ReceiveChannel, buzz: ReceiveChannel) { select { // means that this select expression does not produce any result fizz.onReceive { value -> // this is the first select clause println("fizz -> '$value'") } buzz.onReceive { value -> // this is the second select clause println("buzz -> '$value'") } } } fun CoroutineScope.fizz() = produce { while (true) { // sends "Fizz" every 300 ms delay(300) send("Fizz") } } fun CoroutineScope.buzz() = produce { while (true) { // sends "Buzz!" every 500 ms delay(500) send("Buzz!") } } val fizz = fizz() val buzz = buzz() repeat(7) { selectFizzBuzz(fizz, buzz) } coroutineContext.cancelChildren() // cancel fizz & buzz coroutines fizz -> 'Fizz' buzz -> 'Buzz!' fizz -> 'Fizz' fizz -> 'Fizz' buzz -> 'Buzz!' fizz -> 'Fizz' buzz -> ‘Buzz!'

Slide 44

Slide 44 text

Dispatchers

Slide 45

Slide 45 text

Dispatchers ௏ܖ౯ਸ प೯ೞח Thread ఋੑਸ ૑੿ Main thread, Work thread ١١

Slide 46

Slide 46 text

Dispatchers launch { // context of the parent, main runBlocking coroutine println("main runBlocking : I'm working in thread $ {Thread.currentThread().name}") } launch(Dispatchers.Default) { // will get dispatched to DefaultDispatcher println("Default : I'm working in thread $ {Thread.currentThread().name}") } launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread println("newSingleThreadContext: I'm working in thread $ {Thread.currentThread().name}") } ૑੿ೞ૑ ঋওӝী ৻ࠗ currentThreadী ٮܲ׮. Work threadীࢲ ز੘ Work thread۽ ࢜۽਍ MyOwnThreadܳ ࢤࢿ

Slide 47

Slide 47 text

suspend

Slide 48

Slide 48 text

suspend suspend ఃਕ٘ܳ ୶оೞৈ ೣࣻ ࠙ೡ suspend ੿੄ೠ ೣࣻח ࢎਊೞӝ ੹ө૑ ز੘ೞ૑ ঋח׮ suspend ೣࣻח CoroutineScope ղীࢲ ݅ ࢎਊೡ ࣻ ੓׮

Slide 49

Slide 49 text

suspend private suspend fun waitOne(): Int { delay(100L) return 100 } private suspend fun waitTwo(): Int { delay(200L) return 200 } @Test fun testTwo() = runBlocking { val one = CoroutineScope(Dispatchers.Default).async { waitOne() } val two = CoroutineScope(Dispatchers.Default).async { waitTwo() } println("wait ${one.await()} ${two.await()}") } 100 ms delay റ 100ਸ ܻఢ 200 ms delay റ 200ਸ ܻఢ ਤ 2ѐ੄ Ѿҗܳ async/awaitਸ ੉ਊೞৈ Ѿҗܳ ୹۱

Slide 50

Slide 50 text

Timer ҳഅ

Slide 51

Slide 51 text

Timer ҳഅ അ੤ दр ޷ې दр ޷ې दр - അ੤ दрਵ۽ അ੤दрਸ ҳೞӝ 1ୡী ೠߣঀ ഐ୹ System.currentTimeMillis() System.currentTimeMillis() + N࠙ റ CoroutineScope উীࢲ ୊ܻ CoroutineScope੄ delay() ഝਊ UI৬ Background ܻ࠙ܳ ਤೠ Dispatchers ੿੄

Slide 52

Slide 52 text

Timer ҳഅ private val simpleDateFormat: SimpleDateFormat = SimpleDateFormat("mm:ss", Locale.getDefault()) private suspend fun timer(timerMillis: Long, timerStartTime: Long) = CoroutineScope(Dispatchers.Default).launch { while (isActive) { val nowRemainingMills: Long = System.currentTimeMillis() - timerStartTime val timeRemainingMills: Long = timerMillis - nowRemainingMills if (timeRemainingMills >= 0) { println("now ${simpleDateFormat.format(timeRemainingMills)}") } else { break } delay(1000L) } } @Test fun testTimer() = runBlocking { val endTime = System.currentTimeMillis() + 10000 val job = timer(endTime - System.currentTimeMillis(), System.currentTimeMillis()) job.join() } 1ୡী ೠߣঀ ഐ୹ೞب۾ ؀ӝ Timer ઙܐೡٸө૑ ؀ӝ

Slide 53

Slide 53 text

suspend

Slide 54

Slide 54 text

RxJava৬ coroutines

Slide 55

Slide 55 text

RxJava৬ coroutines

Slide 56

Slide 56 text

RxJava৬ coroutines 처음 학습 비용이 높다 수 많은 라이브러리 활용 가능 예제도 많고, 문서도 잘 되어있다. 처음 학습 비용이 낮다 아직은 부족한 라이브러리 직접 만들 수 있고, 문서도 잘 되어있다

Slide 57

Slide 57 text

private var currentIndex = 0 fab.setOnClickListener { clickEventSubject.onNext(it) } clickEventSubject .throttleFirst(500, TimeUnit.MICROSECONDS) .observeOn(Schedulers.io()) .map { currentIndex++ } .switchMap { Observable.zip(Observable.range(0, 10), Observable.interval(200, TimeUnit.MILLISECONDS), BiFunction { range, _ -> 10 - range }) } .observeOn(AndroidSchedulers.mainThread()) .subscribe({ index -> tv_message.text = "Now index $currentIndex Countdown $index" }, {}) RxJava৬ coroutines var currentIndex = 0 fab.setOnClickListener { CoroutineScope(Dispatchers.Default).launch { val job = launch(Dispatchers.Main) { 10.countDown(++currentIndex) } job.join() } } private suspend fun Int.countDown(currentIndex: Int) { for (index in this downTo 1) { // countdown from 10 to 1 tv_message.text = "Now index $currentIndex Countdown $index" // update text delay(200) } Log.i("TEMP", "Now index $currentIndex Done!") } private val clickEventSubject = XXSubject.create() CoroutineScope Dispatchers.Default launch Observable.interval(200, TimeUnit.MILLISECONDS) Observable.range(0, 10) observeOn(AndroidSchedulers.mainThread()) tv_message.text = "Now index $currentIndex Countdown $index" for (index in this downTo 1) delay(200) .observeOn(Schedulers.io())

Slide 58

Slide 58 text

RxJava৬ coroutines RxJava Coroutines ੿੄ Flowable, Observable, Single ١ CoroutineScope, GlobalScope ੹୓ झாે۞ ૑੿ subscribeOn(schedulers.io()) ࢤࢿ੗ীࢲ ૑੿ झாે۞ ߸҃ observeOn(schedulers.io()) launch, actor ١ ੿੄ೡٸ ૑੿ झாે۞ ߧਤ observeOnীࢲ ੿੄ೠ ׮਺ࠗఠ ݽف ੸ਊ {} উীࢲ݅ ੸ਊ द੘ subscribe ੿੄о ੓যঠ ز੘ ૊द प೯

Slide 59

Slide 59 text

Android Coroutines ഝਊ

Slide 60

Slide 60 text

Android Coroutines CoroutineScope을 상속 받는 Activity/Fragment/ViewModel을 구현 Default context 지정하고, Job을 통해 release 처리

Slide 61

Slide 61 text

abstract class CoroutineScopeActivity : AppCompatActivity(), CoroutineScope { private val job: Job = Job() override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job override fun onDestroy() { super.onDestroy() job.cancel() } } Android Coroutines onDestroy()에서 job을 종료하도록 한다. Job을 미리 생성하여 CoroutineContext에 미리 지정 할 수 있다. Activity에서 사용할 기본 Context를 정의한다.

Slide 62

Slide 62 text

Android Coroutines abstract class CoroutineScopeActivity : CoroutineScopeActivity() { launch { // UI Threadীࢲ ୊ܻ } launch(Dispatchers.Default) { // Default Threadীࢲ ୊ܻ } actor { // UI Threadীࢲ event ୊ܻ for (event in channel) action(event) } actor(Dispatchers.Default) { // Default Theadীࢲ ୊ܻ for (event in channel) action(event) } } launch, actor 등에서는 언제든지 Thread type을 변경할 수 있다.

Slide 63

Slide 63 text

Androidীࢲ ਬਊೠ onClick ୊ܻೞӝ

Slide 64

Slide 64 text

Androidীࢲ ਬਊೠ onClick ୊ܻೞӝ

Slide 65

Slide 65 text

਋ܻо ਗೞח दաܻয় 단 1번만 실행해야 한다는 가정 사용자가 버튼을 누른다 애니메이션을 실행(Progress)/버튼 비활성화 완료하면 애니메이션 종료/버튼 활성화 문제 1. 버튼 비활성화 네트워크 시도 중 오류 발생에 따른 버튼 활성화가 쉽지 않음 문제 2. RxJava 활용으로 버튼 비활성화 대신 시간을 지정 일정 시간마다 버튼이 눌릴 수 있어 결국 중복 이벤트 발생 문제 3. 원하는 이벤트 종료 때까지 애니메이션 처리 사용자 거부감

Slide 66

Slide 66 text

੉۠ ޙઁܳ Coroutineਵ۽ ೧Ѿ೧ࠁ੗ GlobalScope + actor

Slide 67

Slide 67 text

Coroutineਵ۽ ೧Ѿ೧ࠁ੗ private fun View.onClick(action: suspend (View) -> Unit) { val event = GlobalScope.actor(Dispatchers.Main) { for (event in channel) action(event) } setOnClickListener { event.offer(it) } } var currentIndex = 0 fab.onClick { 10.countDown(currentIndex++) } 1. Singletone의 GlobalScope 활용 2. actor 이용 event 받기 3. actor에 offer로 event 보내기 4. 받은 event를 Higher-Order function으로 넘겨서 정의하도록 한다. 6. 람다 표현으로 countDown 구현 5. 이때 Higher-Order function 정의는 suspend가 포함되어야 한다

Slide 68

Slide 68 text

Coroutineਵ۽ ೧Ѿ೧ࠁ੗

Slide 69

Slide 69 text

Androidীࢲ ਬਊೠ onClick ୊ܻೞӝ Higher-Order function + kotlin Extensions을 활용 GlobalScope을 활용하거나, CoroutineScope을 활용 할 수 있다 동작 범위에 따라서 GlobalScope, CoroutineScope을 선택함이 좋다

Slide 70

Slide 70 text

open class CoroutineUIEvent(private val bgBody: suspend (item: E) -> R, private val dispatcherProvider: DispatchersProviderSealed = DispatchersProvider, private val job: Job? = null) { private lateinit var uiSender: SendChannel @ObsoleteCoroutinesApi open fun initUiSender(uiBody: (item: R) -> Unit): CoroutineUIEvent { uiSender = CoroutineScope(dispatcherProvider.main + (job ?: EmptyCoroutineContext)).actor { this.channel.map(context = dispatcherProvider.default, transform = bgBody).consumeEach(uiBody) } return this } open fun offer(element: E) { if (::uiSender.isInitialized) { uiSender.offer(element) } } } onClickਸ ୊ܻೞӝਤೠ SendChannelਸ ୶о UiSender ୡӝചܳ ਤೠ ௏٘ CoroutineScopeਵ۽ ୡӝച offer۽ ׮਺ ੉߮౟ܳ ੹׳ೠ׮ Androidীࢲ ਬਊೠ onClick ୊ܻೞӝ

Slide 71

Slide 71 text

/** * Button onClick and background body. * @param dispatcherProvider : default Default thread and main thread * @param job : default null, add job * @param bgBody : background thread */ fun createUiEvent(dispatcherProvider: DispatchersProviderSealed = DispatchersProvider, job: Job? = null, bgBody: suspend (item: E) -> R): CoroutineUIEvent = CoroutineUIEvent(bgBody, dispatcherProvider, job) /** * run on ui thread. * @param uiBody run ui thread body */ @ObsoleteCoroutinesApi infix fun CoroutineUIEvent.runUi(uiBody: (item: R) -> Unit): CoroutineUIEvent = this.initUiSender(uiBody) UI Eventܳ औѱ ٜ݅ӝ ਤೠ ௏٘੉ݴ, BG ॳۨ٘ ୊ܻ UI ॳۨ٘ীࢲ ࢎਊೡ ௏٘ ࠶ۅਸ ୡӝച Androidীࢲ ਬਊೠ onClick ୊ܻೞӝ

Slide 72

Slide 72 text

fab.onClick { gitHubService.contributors(tv_owner.text.toString(), tv_repo.text.toString()) .onErrorReturn { mutableListOf(Contributor("", 0)) } .doOnError { tv_message.text = "Search error ${it.message}" } .await() }.runUi { for ((name, contributions) in it) { tv_message.text = "$name as $contributions contributions!" } } BG Theadীࢲ ؘ੉ఠܳ ࠛ۞ৡ׮ UI Threadীࢲ View јन Androidীࢲ ਬਊೠ onClick ୊ܻೞӝ

Slide 73

Slide 73 text

implementation “org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.10” implementation “org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1” implementation “androidx.appcompat:appcompat:1.0.1” implementation “androidx.lifecycle:lifecycle-extensions:2.0.0" implementation 'tech.thdev.coroutines:coroutines-extensions:$last_version' https://thdev.tech/CoroutinesUIExtensions/ Android Coroutines

Slide 74

Slide 74 text

Android RxJava৬ ೣԋ ࢎਊೞӝ

Slide 75

Slide 75 text

Android RxJava৬ ೣԋ ࢎਊೞӝ https://github.com/Kotlin/kotlinx.coroutines/blob/master/reactive/ README.md kotlinx-coroutines-reactive -- utilities for Reactive Streams kotlinx-coroutines-reactor -- utilities for Reactor kotlinx-coroutines-rx2 -- utilities for RxJava 2.x

Slide 76

Slide 76 text

interface GitHub { @GET("/repos/{owner}/{repo}/contributors") fun contributors( @Path("owner") owner: String, @Path("repo") repo: String ): Single> @GET("users/{user}/repos") fun listRepos(@Path("user") user: String): Single> } val github = retrofit.create(GitHub::class.java) val contributors = github.contributors("JetBrains", "Kotlin") .await().take(10) RxJava੄ ਽׹ਸ ӝ׮ܻҊ, coroutinesਵ۽ return Android RxJava৬ ೣԋ ࢎਊೞӝ

Slide 77

Slide 77 text

хࢎ೤פ׮. Taehwan [email protected] https://thdev.tech