@@ -408,47 +408,40 @@ during UI freeze.
408
408
### Structured concurrency, lifecycle and coroutine parent-child hierarchy
409
409
410
410
A typical UI application has a number of elements with a lifecycle. Windows, UI controls, activities, views, fragments
411
- and other visual elements are created and destroyed. A long-running coroutine, performing some IO or a background
412
- computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage
411
+ and other visual elements are created and destroyed. A long-running coroutine, performing some IO or a background
412
+ computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage
413
413
collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore.
414
414
415
- The natural solution to this problem is to associate a [ Job] object with each UI object that has a lifecycle and create
416
- all the coroutines in the context of this job. But passing associated job object to every coroutine builder is error-prone,
417
- it is easy to forget it. For this purpose, [ CoroutineScope] interface could be implemented by UI owner, and then every
418
- coroutine builder defined as an extension on [ CoroutineScope] inherits UI job without explicitly mentioning it.
419
- For the sake of simplicity, [ MainScope()] factory can be used. It automatically provides ` Dispatchers.Main ` and parent
420
- job.
415
+ The natural solution to this problem is to associate a [ CoroutineScope] object with each UI object that has a
416
+ lifecycle and create all the coroutines in the context of this scope.
417
+ For the sake of simplicity, [ MainScope()] factory can be used. It automatically provides ` Dispatchers.Main ` and
418
+ a parent job for all the children coroutines.
419
+
420
+ For example, in Android application an ` Activity ` is initially _ created_ and is _ destroyed_ when it is no longer
421
+ needed and when its memory must be released. A natural solution is to attach an
422
+ instance of a ` CoroutineScope ` to an instance of an ` Activity ` :
421
423
422
- For example, in Android application an ` Activity ` is initially _ created_ and is _ destroyed_ when it is no longer
423
- needed and when its memory must be released. A natural solution is to attach an
424
- instance of a ` Job ` to an instance of an ` Activity ` :
425
424
<!-- - CLEAR -->
426
425
427
426
``` kotlin
428
- abstract class ScopedAppActivity : AppCompatActivity (), CoroutineScope by MainScope() {
427
+ class MainActivity : AppCompatActivity () {
428
+ private val scope = MainScope ()
429
+
429
430
override fun onDestroy () {
430
431
super .onDestroy()
431
- cancel() // CoroutineScope.cancel
432
+ scope. cancel()
432
433
}
433
- }
434
- ```
435
-
436
- Now, an activity that is associated with a job has to extend ScopedAppActivity
437
-
438
- ``` kotlin
439
- class MainActivity : ScopedAppActivity () {
440
434
441
- fun asyncShowData () = launch { // Is invoked in UI context with Activity's job as a parent
435
+ fun asyncShowData () = scope. launch { // Is invoked in UI context with Activity's scope as a parent
442
436
// actual implementation
443
437
}
444
438
445
439
suspend fun showIOData () {
446
- val deferred = async (Dispatchers .IO ) {
447
- // impl
440
+ val data = withContext (Dispatchers .IO ) {
441
+ // compute data in background thread
448
442
}
449
443
withContext(Dispatchers .Main ) {
450
- val data = deferred.await()
451
- // Show data in UI
444
+ // Show data in UI
452
445
}
453
446
}
454
447
}
@@ -457,43 +450,14 @@ class MainActivity : ScopedAppActivity() {
457
450
Every coroutine launched from within a ` MainActivity ` has its job as a parent and is immediately cancelled when
458
451
activity is destroyed.
459
452
460
- To propagate activity scope to its views and presenters, multiple techniques can be used:
461
- - [ coroutineScope] builder to provide a nested scope
462
- - Receive [ CoroutineScope] in presenter method parameters
463
- - Make method extension on [ CoroutineScope] (applicable only for top-level methods)
464
-
465
- ``` kotlin
466
- class ActivityWithPresenters : ScopedAppActivity () {
467
- fun init () {
468
- val presenter = Presenter ()
469
- val presenter2 = ScopedPresenter (this )
470
- }
471
- }
472
-
473
- class Presenter {
474
- suspend fun loadData () = coroutineScope {
475
- // Nested scope of outer activity
476
- }
477
-
478
- suspend fun loadData (uiScope : CoroutineScope ) = uiScope.launch {
479
- // Invoked in the uiScope
480
- }
481
- }
482
-
483
- class ScopedPresenter (scope : CoroutineScope ): CoroutineScope by scope {
484
- fun loadData () = launch { // Extension on ActivityWithPresenters's scope
485
- }
486
- }
487
-
488
- suspend fun CoroutineScope.launchInIO () = launch(Dispatchers .IO ) {
489
- // Launched in the scope of the caller, but with IO dispatcher
490
- }
491
- ```
453
+ > Note, that Android has first-party support for coroutine scope in all entities with the lifecycle.
454
+ See [ the corresponding documentation] ( https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope ) .
492
455
493
456
Parent-child relation between jobs forms a hierarchy. A coroutine that performs some background job on behalf of
494
- the view and in its context can create further children coroutines. The whole tree of coroutines gets cancelled
457
+ the activity can create further children coroutines. The whole tree of coroutines gets cancelled
495
458
when the parent job is cancelled. An example of that is shown in the
496
459
[ "Children of a coroutine"] ( ../docs/coroutine-context-and-dispatchers.md#children-of-a-coroutine ) section of the guide to coroutines.
460
+
497
461
<!-- - CLEAR -->
498
462
499
463
### Blocking operations
@@ -649,7 +613,6 @@ After delay
649
613
[ Job.cancel ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html
650
614
[ CoroutineScope ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
651
615
[ MainScope() ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html
652
- [ coroutineScope ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
653
616
[ withContext ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html
654
617
[ Dispatchers.Default ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
655
618
[ CoroutineStart ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/index.html
0 commit comments