Share
Embed
Droid knights 2019 - Kotlin Corotuines
It deals with Kotlin coroutines.
CoroutineScope, GlobalScope, Android Lifecycle Following, Job, Poor Job, Good Job, supervisorScope, suspendCancellableCoroutine
TaeHwan
April 05, 2019
Transcript
72 slides
-
• GDG Seoul • ٘۽٘ աஎ • )
٧ܻߡܻয۽ܻই উ٘۽٘ ѐߊ • അ) য়ಕ উ٘۽٘ ѐߊ • Blog : Է ݆ ѐߊо غ! (https://thdev.tech) ࣗѐ -
$PSPVUJOF4DPQF $PSPVUJOFTDPEF @Test fun testCoroutines() = runBlocking { CoroutineScope(Dispatchers.Default) .launch
{ } delay(1500) } println("Hello") delay(1000) println("droid knights!!!") -
$PSPVUJOF4DPQF $PSPVUJOFTDPEF @Test fun testCoroutines() = runBlocking { CoroutineScope(Dispatchers.Default) .launch
{ } delay(1500) } Hello println("Hello") delay(1000) println("droid knights!!!") Droid knights!!! delay 1 sec -
$PSPVUJOF4DPQF $PSPVUJOF4DPQFۆ ↟$PSPVUJOFTࢎਊߧਤ ↟ݽٚ$PSPVUJOFT$PSPVUJOF4DPQF࢚ࣘ public interface CoroutineScope { /* *
Context of this scope. */ public val coroutineContext: CoroutineContext } -
$PSPVUJOF4DPQF $PSPVUJOF4DPQFز CoroutineScope(Dispatchers.IO).launch { val jobOne = launch { //
jobOne } val jobTwo = launch { // jobTwo launch(Dispatchers.Main) { // jobTwo UI Thread } } jobOne.join() jobTwo.join() } jobOne jobTwo jobTwo(Main) jobOne / jobTwo زद प೯ Job two উীࢲ UI ܻ CoroutineScope jobOne/jobTwo ઙܐ ӝ -
$PSPVUJOF4DPQF (MPCBM4DPQFۆ public object GlobalScope : CoroutineScope { /* *
Returns [EmptyCoroutineContext]. */ override val coroutineContext: CoroutineContext get() = EmptyCoroutineContext } (MPCBM4DPQF*0 XPSLFS UISFBEীࢲز 5ISFBE<%FGBVMU%JTQBUDIFSXPSLFS!DPSPVUJOF NBJO> -
$PSPVUJOFT $PSPVUJOFTDPEF class MyActivity : AppCompatActivity(), CoroutineScope { // Jobਸ
١۾ೡ ࣻ ب۾ ୡӝച private val job: Job = SupervisorJob() // ӝࠄ Main Thread ৬ jobਸ ೣԋ ୡӝച override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job // স ؍ ݽٚ jobਸ ઙ childrenਸ ઙܐ ܻ override fun onDestroy() { super.onDestroy() job.cancel() // Cancel job on activity destroy. // After destroy all children jobs will be cancelled automatically } } -
↟+PCJOUFSGBDF۽غয +PC $PSPVUJOFTKPC public interface Job : CoroutineContext.Element { //
... public val isActive: Boolean public val isCompleted: Boolean // ... } -
↟+PCJOUFSGBDF۽غয ↟MBVODI١ࢎਊदKPCёܳSFUVSO // return jobਵ۽ ஶ܀ೞӝ val job = CoroutineScope(
Dispatchers.Default).launch { // … } job.join() ↟߹بKPCਸࢤࢿೡࣻ val job = Job() CoroutineScope( Dispatchers.Default + job) .launch { } +PC $PSPVUJOFTKPC -
+PC ↟+PCJOUFSGBDF۽غয ↟MBVODI١ࢎਊदKPCёܳSFUVSO ↟߹بKPCਸࢤࢿೡࣻ ↟KPCਊदDPSPVUJOFDBODFM TUBSU KPJO١ਸೡࣻ ↟TUBSUDPSPVUJOFز࢚కഛੋ ↟USVFز GBMTFળ࠺৮ܐ
$PSPVUJOFTKPC ↟KPJODPSPVUJOF4DPQFزઙܐܳӝ ↟DBODFMDPSPVUJOF4DPQFزਸઙܐਬب ↟DBODFM"OE+PJODPSPVUJOF4DPQFزઙܐਃೞҊ ӝ ↟DBODFM$IJMESFODPSPVUJOF4DPQFDIJMESFOزਸઙܐਬب -
+PC $PSPVUJOFTKPCTUBUVT New Active Start Completing Complete Completed Finish Cancelling
Cancelled cancel/fail Finish -
+PC $PSPVUJOFTKPCTUBUVTଵҊਊद State isActive isCompleted isCancelled New (optional initial state)
FALSE FALSE FALSE Active (default initial state) TRUE FALSE FALSE Completing (transient state) TRUE FALSE FALSE Cancelling (transient state) FALSE FALSE TRUE Cancelled (final state) FALSE TRUE TRUE Completed (final state) FALSE TRUE FALSE -
@Test fun testCoroutines() = runBlocking { val job = Job()
CoroutineScope(Dispatchers.Default + job).launch { CoroutineScope(Dispatchers.Default).launch { println("New CoroutineScope for start") for (index in 0..20) { if (isActive) { println("New CoroutineScope index $index") delay(1) } else { break } } println("New CoroutineScope for end") } val defaultJob = launch { println("Default for start") for (index in 0..10) { if (isActive) { println("Default index $index") +PC +PCੜޅࢎਊೠDPEF -
@Test fun testCoroutines() = runBlocking { val job = Job()
CoroutineScope(Dispatchers.Default + job).launch { CoroutineScope(Dispatchers.Default).launch { println("New CoroutineScope for start") for (index in 0..20) { if (isActive) { println("New CoroutineScope index $index") delay(1) } else { break } } println("New CoroutineScope for end") } val defaultJob = launch { println("Default for start") for (index in 0..10) { if (isActive) { println("Default index $index") +PC +PCੜޅࢎਊೠDPEF Main CoroutineScope ୡӝച New CoroutineScope ୡӝച दীח job ನೣೞ ঋ -
println("New CoroutineScope index $index") delay(1) } else { break }
} println("New CoroutineScope for end") } val defaultJob = launch { println("Default for start") for (index in 0..10) { if (isActive) { println("Default index $index") delay(1) } else { break } } println("Default for end") } defaultJob.join() } job.cancel() delay(30) // 30ms test only. } +PC +PCੜޅࢎਊೠDPEF Main CoroutinesCope दҗ زदী cancel ഐೠ -
+PC /FX$PSPVUJOF4DPQFGPSTUBSU /FX$PSPVUJOF4DPQFJOEFY /FX$PSPVUJOF4DPQFJOEFY /FX$PSPVUJOF4DPQFJOEFY /FX$PSPVUJOF4DPQFJOEFY j /FX$PSPVUJOF4DPQFJOEFY /FX$PSPVUJOF4DPQFGPSFOE +PCੜޅࢎਊೠ$PEFѾҗ
Default Job द റ द ઙܐغ %FGBVMUGPSTUBSU %FGBVMUGPSFOE New CoroutineScope Default Jobҗ ޖҙೞѱ ز -
+PC +PCੜޅࢎਊೠޙઁܳইࠁ ↟$PSPVUJOF4DPQFઙܐ ↟/FX$PSPVUJOF4DPQF҅ࣘস ↟/FX$PSPVUJOF4DPQFਸઁযೡߑߨহ CoroutineScope Default Scope New CoroutineScope
loop 1. CoroutinScope ઙܐ 2. CoroutineScope ઙܐ೧ب ҅ࣘ ز -
@Test fun testCoroutines() = runBlocking { val job = Job()
CoroutineScope(Dispatchers.Default + job).launch { CoroutineScope(Dispatchers.Default + job).launch { println("New CoroutineScope for start") for (index in 0..20) { if (isActive) { println("New CoroutineScope index $index") delay(1) } else { break } } println("New CoroutineScope for end") } val defaultJob = launch { println("Default for start") for (index in 0..10) { if (isActive) { println("Default index $index") delay(1) +PC ೧Ѿ New CoroutineScope ୡӝച दীب jobਸ ୶о -
+PC /FX$PSPVUJOF4DPQFGPSTUBSU /FX$PSPVUJOF4DPQFJOEFY /FX$PSPVUJOF4DPQFJOEFY +PC߸҃ীೠѾҗ %FGBVMUGPSTUBSU %FGBVMUJOEFY Default scope 0
ө ۱ ೞҊ ઙܐ New CoroutineScope 1 ө ۱ ೞҊ ઙܐ -
+PC +PCਸખ؊ಝࠁ private val job = Job() private val coroutineScope
= CoroutineScope(Dispatchers.IO + job) @Test fun testException() = runBlocking { with(coroutineScope) { println("Parent scope") val firstChildren = launch { println("First children is failing") throw Exception("First children is exception") } val secondChildren = launch { println("Second children is success") delay(500) println("Second children is delay 500ms") } firstChildren.join() secondChildren.join() } } -
+PC +PCਸખ؊ಝࠁ private val job = Job() private val coroutineScope
= CoroutineScope(Dispatchers.IO + job) @Test fun testException() = runBlocking { with(coroutineScope) { println("Parent scope") val firstChildren = launch { println("First children is failing") } val secondChildren = launch { println("Second children is success") delay(500) println("Second children is delay 500ms") } firstChildren.join() secondChildren.join() } } Exception ъઁ ߊࢤ throw Exception("First children is exception") -
+PC 1BSFOUTDPQF 'JSTUDIJMESFOJTGBJMJOH &YDFQUJPOJOUISFBE%FGBVMU%JTQBUDIFSXPSLFS!DPSPVUJOF&YDFQUJPOJOUISFBE %FGBVMU%JTQBUDIFSXPSLFS!DPSPVUJOFKBWBMBOH&YDFQUJPO'JSTUDIJMEJTFYDFQUJPO BU UFDIUIEFWDPSPVUJOFTTDPQF6*$PSPVUJOF4DPQF5FTUUFTU&YDFQUJPODIJME+PCJOWPLF 4VTQFOE 6*$PSPVUJOF4DPQF5FTULU
j KBWBMBOH&YDFQUJPO'JSTUDIJMESFOJTFYDFQUJPO j +PCਸખ؊ಝࠁ -
+PC 1BSFOUTDPQF 'JSTUDIJMESFOJTGBJMJOH &YDFQUJPOJOUISFBE%FGBVMU%JTQBUDIFSXPSLFS!DPSPVUJOF&YDFQUJPOJOUISFBE %FGBVMU%JTQBUDIFSXPSLFS!DPSPVUJOFKBWBMBOH&YDFQUJPO'JSTUDIJMEJTFYDFQUJPO BU UFDIUIEFWDPSPVUJOFTTDPQF6*$PSPVUJOF4DPQF5FTUUFTU&YDFQUJPODIJME+PCJOWPLF 4VTQFOE 6*$PSPVUJOF4DPQF5FTULU
j KBWBMBOH&YDFQUJPO'JSTUDIJMESFOJTFYDFQUJPO j +PCਸખ؊ಝࠁ Parent scope ઙܐغ. -
+PC 5IFNPTUCBTJDJOTUBODFTPG+PCBSFDSFBUFEXJUIMBVODIDPSPVUJOFCVJMEFSPSXJUIB+PC GBDUPSZGVODUJPO#ZEFGBVMU BGBJMVSFPGBOZPGUIFKPCsTDIJMESFOMFBETUPBOJNNFEJBUFMZ GBJMVSFPGJUTQBSFOUBOEDBODFMMBUJPOPGUIFSFTUPGJUTDIJMESFO5IJTCFIBWJPSDBOCF DVTUPNJ[FEVTJOH4VQFSWJTPS+PC ޙࢲ۽ഛੋ೧ࠁ #ZEFGBVMU BGBJMVSFPGBOZPGUIFKPCsTDIJMESFOMFBETUPBOJNNFEJBUFMZ
GBJMVSFPGJUTQBSFOUBOEDBODFMMBUJPOPGUIFSFTUPGJUTDIJMESFO ޙࢲ : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/ +PC DIJMESFOೞաоઙܐغݶ ࠗݽীѱాೞҊ ࠗݽחݽٚDIJMESFOਸઙܐೞҊ ӝب ઙܐೠ -
+PC ޙࢲܳӝ߈ਵ۽٘ܳदಝࠁ private val job = Job() private val coroutineScope
= CoroutineScope(Dispatchers.IO + job) @Test fun testException() = runBlocking { with(coroutineScope) { println("Parent scope") val firstChildren = launch { println("First children is failing") throw Exception("First children is exception") } val secondChildren = launch { println("Second children is success") delay(500) println("Second children is delay 500ms") } firstChildren.join() secondChildren.join() } } Parent Scope ܲ childrenਸ ݽف ઙܐೞҊ, ӝب ઙܐೠ. Exception ߊࢤ द ࠗݽীѱ ా -
+PC $PSPVUJOF4DPQ/ѐDIJMESFOਸоࣻ ܻೞݶ Main CoroutineScope JobOne JobTwo … JobN 1ѐ
children Exception ߊࢤ ݽٚ childrenҗ ࠗݽ ݽف ઙܐ ೞաۄبલਵݶࠗݽীѱాೞҊݽفઙܐೠ -
+PC दޙࢲܳࠁ 5IFNPTUCBTJDJOTUBODFTPG+PCBSFDSFBUFEXJUIMBVODIDPSPVUJOFCVJMEFSPSXJUIB+PC GBDUPSZGVODUJPO#ZEFGBVMU BGBJMVSFPGBOZPGUIFKPCsTDIJMESFOMFBETUPBOJNNFEJBUFMZ GBJMVSFPGJUTQBSFOUBOEDBODFMMBUJPOPGUIFSFTUPGJUTDIJMESFO ޙࢲ : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/
5IJTCFIBWJPSDBOCF DVTUPNJ[FEVTJOH4VQFSWJTPS+PC -
Job() +PC 4VQFSWJTPS+PCਊೞӝ private val job = private val coroutineScope
= CoroutineScope(Dispatchers.IO + job) @Test fun testException() = runBlocking { with(coroutineScope) { println("Parent scope") val firstChildren = launch { println("First children is failing") throw Exception("First children is exception") } val secondChildren = launch { println("Second children is success") delay(500) println("Second children is delay 500ms") } firstChildren.join() secondChildren.join() } } SupervisorJob() private val coroutineScope = CoroutineScope(Dispatchers.IO + job) @Test fun testException() = runBlocking { with(coroutineScope) { println("Parent scope") val firstChildren = launch { println("First children is failing") throw Exception("First children is exception") } val secondChildren = launch { println("Second children is success") delay(500) println("Second children is delay 500ms") } firstChildren.join() secondChildren.join() } } -
4VQFSWJTPS+PC 4VQFSWJTPS+PCਊѾҗ 1BSFOUKPC 'JSTUDIJMEJTGBJMJOH 4FDPOEDIJMEJTTVDDFTT j &YDFQUJPOJOUISFBE%FGBVMU%JTQBUDIFSXPSLFS!DPSPVUJOFKBWBMBOH&YDFQUJPO'JSTU DIJMEJTFYDFQUJPO BU UFDIUIEFWDPSPVUJOFTTDPQF6*$PSPVUJOF4DPQF5FTUUFTU&YDFQUJPODIJME+PCJOWPLF4VTQ
FOE 6*$PSPVUJOF4DPQF5FTULU j 4FDPOEDIJMEJTEFMBZNT 1BSFOUTDPQF 'JSTUDIJMESFOJTGBJMJOH 4FDPOEDIJMESFOJTTVDDFTT 4FDPOEDIJMESFOJTEFMBZNT -
public fun Job(parent: Job? = null): Job = JobImpl(parent) public
fun SupervisorJob(parent: Job? = null) : Job = SupervisorJobImpl(parent) +PC +PCҗ4VQFSWJTPS+PCਸୡӝചೞח٘ -
internal class JobImpl(parent: Job? = null) : JobSupport(true) { init
{ initParentJobInternal(parent) } override val cancelsParent: Boolean get() = true override val onCancelComplete get() = true override val handlesException: Boolean get() = false } +PC +PC*NQM4VQFSWJTPS+PC*NQMҳഅܳࠁ private class SupervisorJobImpl(parent: Job?) : JobSupport(true) { init { initParentJobInternal(parent) } override val cancelsParent: Boolean get() = true override val onCancelComplete get() = true override val handlesException: Boolean get() = false } override fun childCancelled(cause: Throwable): Boolean = false Child ஂࣗ ࠁܳ parentীѱ ࣠ೡ ৈࠗܳ Ѿ -
private fun cancelParent(cause: Throwable): Boolean { if (cause is CancellationException)
return true if (!cancelsParent) return false return parentHandle?.childCancelled(cause) == true } +PC DIJME$BODFMMFEחয٣ࢲࠗܳө ৈӝীࢲ Ѿೞৈ ܻೠ -
&YDFQUJPOܻ߮ য়ܨ٘दഛੋ j &YDFQUJPOJOUISFBE%FGBVMU%JTQBUDIFSXPSLFS!DPSPVUJOFKBWBMBOH&YDFQUJPO'JSTU DIJMEJTFYDFQUJPO BU UFDIUIEFWDPSPVUJOFTTDPQF6*$PSPVUJOF4DPQF5FTUUFTU&YDFQUJPODIJME+PCJOWPLF4VTQ FOE 6*$PSPVUJOF4DPQF5FTULU j
1BSFOUTDPQF 'JSTUDIJMESFOJTGBJMJOH 4FDPOEDIJMESFOJTTVDDFTT 4FDPOEDIJMESFOJTEFMBZNT -
val errorHandler = CoroutineExceptionHandler { coroutineContext, exception -> }
&YDFQUJPOܻ߮ $PSPVUJOF&YDFQUJPO)BOEMFSࢎਊೞӝ // log to Crashlytics, logcat, etc.; can be dependency injected when (exception) { is NetworkErrorException -> { } is NetworkOnMainThreadException -> { } is CustomException -> { } else -> throw exception } ೞ ঋ exception Ӓ۽ ࠁղ -
val errorHandler = CoroutineExceptionHandler { coroutineContext, exception -> // log
to Crashlytics, logcat, etc.; can be dependency injected } &YDFQUJPOܻ߮ $PSPVUJOF&YDFQUJPO)BOEMFSࢎਊೞӝ private val job = SupervisorJob() private val coroutineScope = CoroutineScope(job + Dispatchers.Default + errorHandler) launch(errorHandler) -
public suspend fun supervisorScope(block: suspend CoroutineScope.() -> ): = block:
suspend CoroutineScope.() -> ژܲয়ܨܻ TVQFSWJTPS4DPQF suspendCoroutineUninterceptedOrReturn { uCont -> val coroutine = SupervisorCoroutine(uCont.context, uCont) coroutine.startUndispatchedOrReturn(coroutine, block) } <R> R R -
@Test fun testException() = runBlocking { with(coroutineScope) println("Parent scope") val
firstChildren = launch { println("First children is failing") throw Exception("First children is exception") } val secondChildren = launch { println("Second children is success") delay(500) println("Second children is delay 500ms") } firstChildren.join() secondChildren.join() } } with(coroutineScope) { ژܲয়ܨܻ TVQFSWJTPS4DPQFࢎਊ supervisorScope { -
ژܲয়ܨܻ TVQFSWJTPS4DPQFࢎਊ 1BSFOUTDPQF 'JSTUDIJMESFOJTGBJMJOH j 4FDPOEDIJMESFOJTTVDDFTT j
4FDPOEDIJMESFOJTEFMBZNT BUPSHKVOJUSVOOFST#MPDL+6OJU$MBTT3VOOFSSVO$IJME #MPDL+6OJU$MBTT3VOOFSKBWB BUDPNJOUFMMJKSUFYFDVUJPOKVOJU+6OJU4UBSUFSNBJO +6OJU4UBSUFSKBWB &YDFQUJPOJOUISFBENBJO!DPSPVUJOFKBWBMBOH&YDFQUJPO'JSTUDIJMESFOJTFYDFQUJPOBU UFDIUIEFWDPSPVUJOFTTDPQF6*$PSPVUJOF4DPQF5FTUUFTU&YDFQUJPOGJSTU$IJMESFOJOWPLF4VTQF OE 6*$PSPVUJOF4DPQF5FTULU -
private class SupervisorCoroutine<R>( parentContext: CoroutineContext, @JvmField val uCont: Continuation<R> )
: AbstractCoroutine<R>(parentContext, true) { override val defaultResumeMode: Int get() = MODE_DIRECT @Suppress("UNCHECKED_CAST") internal override fun onCompletionInternal(state: Any?, mode: Int, suppressed: Boolean) { if (state is CompletedExceptionally) uCont.resumeUninterceptedWithExceptionMode(state.cause, mode) else uCont.resumeUninterceptedMode(state as R, mode) } } ژܲয়ܨܻ TVQFSWJTPS4DPQFDIJME$BODFMMFEܻ override fun childCancelled(cause: Throwable): Boolean = false -
ೞա؊ 4VTQFOETDPSPVUJOFTJNJMBSUPTVTQFOE$PSPVUJOF CVUQSPWJEFBOJNQMFNFOUBUJPOPG $BODFMMBCMF$POUJOVBUJPOUPUIFCMPDL5IJTGVODUJPOUISPXT$BODFMMBUJPO&YDFQUJPOJGUIF DPSPVUJOFJTDBODFMMFEPSDPNQMFUFEXIJMFTVTQFOEFE TVTQFOE$BODFMMBCMF$PSPVUJOF ޙࢲ : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/suspend-cancellable-coroutine.html TVTQFOE$PSPVUJOFҗਬࢎ
$PSPVUJOFੌदژחஂࣗद$BODFMMBUJPO&YDFQUJPOਸੌਵఅ TJNJMBSUPTVTQFOE$PSPVUJOF $BODFMMBUJPO&YDFQUJPOJGUIF DPSPVUJOFJTDBODFMMFEPSDPNQMFUFEXIJMFTVTQFOEFE -
public suspend fun <T : Any> Call<T>.await(): T { return
enqueue(object : Callback<T> { override fun onResponse(call: Call<T>?, response: Response<T?>) { if (response.isSuccessful) { response.body() ?: throw NullPointerException("Response body is null: $response") } else { throw HttpException(response) } }) } override fun onFailure(call: Call<T>, t: Throwable) { if (continuation.isCancelled) return } }) } } ೞա؊ 3FUSPGJUTVTQFOE$BODFMMBCMF$PSPVUJOF 3FUSPGJUIUUQTLPUMJOHJUIVCJPLPUMJOYDPSPVUJOFTLPUMJOYDPSPVUJOFTDPSFLPUMJOYDPSPVUJOFTDPSPVUJOFFYDFQUJPOIBOEMFS suspendCancellableCoroutine { continuation -> continuation.resumeWith(runCatching { continuation.resumeWithException(t) registerOnCompletion(continuation) -
private fun Call<*>.registerOnCompletion(continuation: CancellableContinuation<*>) { continuation.invokeOnCancellation { try { cancel()
} catch (ex: Throwable) { //Ignore cancel exception } } } ೞա؊ 3FUSPGJUTVTQFOE$BODFMMBCMF$PSPVUJOF 3FUSPGJUIUUQTLPUMJOHJUIVCJPLPUMJOYDPSPVUJOFTLPUMJOYDPSPVUJOFTDPSFLPUMJOYDPSPVUJOFTDPSPVUJOFFYDFQUJPOIBOEMFS -
য়ಕ ۄ(Android) ѐߊ ੑ ૣ दр ܘ ࢿҗࠁ ؊ ষաҊ
֥ۄ ࣁ࢚ਸ য়ಕ৬ ೣԋ ৌযт, ߄۽ नਸ ӝ݀פ! ਗೞӝ ◆ ਗѺ - নೠ UI ѐߊ ҃җ ࢎਊ ҃ী ೠ Ө ೧ - য়ࣗझী Ѣࠗх হҊ ೧بо ֫ ࠙ - ֤ܻҊ ҅ੋ ޙઁ೧Ѿ מ۱ - ബҗੋ ࢎ׳, ࢚ߑਸ ҃ೞח ழޭפா࣌ מ۱