Skip to content

Commit b367fae

Browse files
qwwdfsadelizarov
authored andcommitted
Update cancellation guide: get rid of explicit Job in general code, promote usage of coroutine scope get rid of counter-intuitive lateinit and getters in coroutine context
1 parent 1660e6a commit b367fae

File tree

3 files changed

+25
-50
lines changed

3 files changed

+25
-50
lines changed

coroutines-guide.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m
3838
* <a name='parental-responsibilities'></a>[Parental responsibilities](docs/coroutine-context-and-dispatchers.md#parental-responsibilities)
3939
* <a name='naming-coroutines-for-debugging'></a>[Naming coroutines for debugging](docs/coroutine-context-and-dispatchers.md#naming-coroutines-for-debugging)
4040
* <a name='combining-context-elements'></a>[Combining context elements](docs/coroutine-context-and-dispatchers.md#combining-context-elements)
41-
* <a name='cancellation-via-explicit-job'></a>[Cancellation via explicit job](docs/coroutine-context-and-dispatchers.md#cancellation-via-explicit-job)
41+
* <a name='getting-it-together'></a>[Getting it together](docs/coroutine-context-and-dispatchers.md#getting-it-together)
4242
* <a name='thread-local-data'></a>[Thread-local data](docs/coroutine-context-and-dispatchers.md#thread-local-data)
4343
<!--- TOC_REF docs/exception-handling.md -->
4444
* <a name='exception-handling'></a>[Exception handling](docs/exception-handling.md#exception-handling)

docs/coroutine-context-and-dispatchers.md

+22-36
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class DispatchersGuideTest {
3030
* [Parental responsibilities](#parental-responsibilities)
3131
* [Naming coroutines for debugging](#naming-coroutines-for-debugging)
3232
* [Combining context elements](#combining-context-elements)
33-
* [Cancellation via explicit job](#cancellation-via-explicit-job)
33+
* [Getting it together](#getting-it-together)
3434
* [Thread-local data](#thread-local-data)
3535

3636
<!--- END_TOC -->
@@ -487,48 +487,43 @@ I'm working in thread DefaultDispatcher-worker-1 @test#2
487487

488488
<!--- TEST FLEXIBLE_THREAD -->
489489

490-
### Cancellation via explicit job
490+
### Getting it together
491491

492492
Let us put our knowledge about contexts, children and jobs together. Assume that our application has
493493
an object with a lifecycle, but that object is not a coroutine. For example, we are writing an Android application
494494
and launch various coroutines in the context of an Android activity to perform asynchronous operations to fetch
495495
and update data, do animations, etc. All of these coroutines must be cancelled when activity is destroyed
496-
to avoid memory leaks.
497-
498-
We manage a lifecycle of our coroutines by creating an instance of [Job] that is tied to
499-
the lifecycle of our activity. A job instance is created using [Job()] factory function when
500-
activity is created and it is cancelled when an activity is destroyed like this:
496+
to avoid memory leaks. We, of course, can manipulate contexts and jobs manually to tie activity's
497+
and coroutines lifecycles, but `kotlinx.coroutines` provides an abstraction that encapsulates that: [CoroutineScope].
498+
You should be already familiar with coroutine scope as all coroutine builders are declared as extensions on it.
501499

500+
We manage a lifecycle of our coroutines by creating an instance of [CoroutineScope] that is tied to
501+
the lifecycle of our activity. `CoroutineScope` instance can be created by [CoroutineScope()] or [MainScope()]
502+
factory functions. The former creates a general-purpose scope, while the latter creates scope for UI applications and uses
503+
[Dispatchers.Main] as default dispatcher:
502504

503505
<div class="sample" markdown="1" theme="idea" data-highlight-only>
504506

505507
```kotlin
506-
class Activity : CoroutineScope {
507-
lateinit var job: Job
508-
509-
fun create() {
510-
job = Job()
511-
}
512-
508+
class Activity {
509+
private val mainScope = MainScope()
510+
513511
fun destroy() {
514-
job.cancel()
512+
mainScope.cancel()
515513
}
516514
// to be continued ...
517515
```
518516

519517
</div>
520518

521-
We also implement [CoroutineScope] interface in this `Actvity` class. We only need to provide an override
522-
for its [CoroutineScope.coroutineContext] property to specify the context for coroutines launched in its
523-
scope. We combine the desired dispatcher (we used [Dispatchers.Default] in this example) and a job:
519+
We can implement [CoroutineScope] interface in this `Actvity` class. The best way to do it is
520+
to use delegation with default factory functions.
521+
We also can combine the desired dispatcher (we used [Dispatchers.Default] in this example) with the scope:
524522

525523
<div class="sample" markdown="1" theme="idea" data-highlight-only>
526524

527525
```kotlin
528-
// class Activity continues
529-
override val coroutineContext: CoroutineContext
530-
get() = Dispatchers.Default + job
531-
// to be continued ...
526+
class Activity : CoroutineScope by CoroutineScope(Dispatchers.Default)
532527
```
533528

534529
</div>
@@ -566,23 +561,13 @@ onto the screen anymore if we wait:
566561
import kotlin.coroutines.*
567562
import kotlinx.coroutines.*
568563

569-
class Activity : CoroutineScope {
570-
lateinit var job: Job
571-
572-
fun create() {
573-
job = Job()
574-
}
564+
class Activity : CoroutineScope by CoroutineScope(Dispatchers.Default) {
575565

576566
fun destroy() {
577-
job.cancel()
567+
cancel() // Extension on CoroutineScope
578568
}
579569
// to be continued ...
580570

581-
// class Activity continues
582-
override val coroutineContext: CoroutineContext
583-
get() = Dispatchers.Default + job
584-
// to be continued ...
585-
586571
// class Activity continues
587572
fun doSomething() {
588573
// launch ten coroutines for a demo, each working for a different time
@@ -598,7 +583,6 @@ class Activity : CoroutineScope {
598583
fun main() = runBlocking<Unit> {
599584
//sampleStart
600585
val activity = Activity()
601-
activity.create() // create an activity
602586
activity.doSomething() // run test function
603587
println("Launched coroutines")
604588
delay(500L) // delay for half a second
@@ -712,7 +696,9 @@ that should be implemented.
712696
[CoroutineScope.coroutineContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/coroutine-context.html
713697
[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html
714698
[CoroutineName]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-name/index.html
715-
[Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job.html
699+
[CoroutineScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html
700+
[MainScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html
701+
[Dispatchers.Main]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html
716702
[asContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/as-context-element.html
717703
[ThreadContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/index.html
718704
<!--- END -->

kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt

+2-13
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,13 @@ package kotlinx.coroutines.guide.context10
88
import kotlin.coroutines.*
99
import kotlinx.coroutines.*
1010

11-
class Activity : CoroutineScope {
12-
lateinit var job: Job
13-
14-
fun create() {
15-
job = Job()
16-
}
11+
class Activity : CoroutineScope by CoroutineScope(Dispatchers.Default) {
1712

1813
fun destroy() {
19-
job.cancel()
14+
cancel() // Extension on CoroutineScope
2015
}
2116
// to be continued ...
2217

23-
// class Activity continues
24-
override val coroutineContext: CoroutineContext
25-
get() = Dispatchers.Default + job
26-
// to be continued ...
27-
2818
// class Activity continues
2919
fun doSomething() {
3020
// launch ten coroutines for a demo, each working for a different time
@@ -40,7 +30,6 @@ class Activity : CoroutineScope {
4030
fun main() = runBlocking<Unit> {
4131
//sampleStart
4232
val activity = Activity()
43-
activity.create() // create an activity
4433
activity.doSomething() // run test function
4534
println("Launched coroutines")
4635
delay(500L) // delay for half a second

0 commit comments

Comments
 (0)