Skip to content

Commit d22136d

Browse files
committed
~ Updates based on review
* Fixed scope examples in guides, added notes on first-party support in Android. * Simplified scopes section in UI guide since it is mostly irrelevant.
1 parent 66a5a7a commit d22136d

File tree

4 files changed

+40
-91
lines changed

4 files changed

+40
-91
lines changed

docs/coroutine-context-and-dispatchers.md

+11-23
Original file line numberDiff line numberDiff line change
@@ -501,21 +501,8 @@ class Activity {
501501

502502
</div>
503503

504-
Alternatively, we can implement the [CoroutineScope] interface in this `Activity` class. The best way to do it is
505-
to use delegation with default factory functions.
506-
We also can combine the desired dispatcher (we used [Dispatchers.Default] in this example) with the scope:
507-
508-
<div class="sample" markdown="1" theme="idea" data-highlight-only>
509-
510-
```kotlin
511-
class Activity : CoroutineScope by CoroutineScope(Dispatchers.Default) {
512-
// to be continued ...
513-
```
514-
515-
</div>
516-
517-
Now, we can launch coroutines in the scope of this `Activity` without having to explicitly
518-
specify their context. For the demo, we launch ten coroutines that delay for a different time:
504+
Now, we can launch coroutines in the scope of this `Activity` using the defined `scope`.
505+
For the demo, we launch ten coroutines that delay for a different time:
519506

520507
<div class="sample" markdown="1" theme="idea" data-highlight-only>
521508

@@ -524,7 +511,7 @@ specify their context. For the demo, we launch ten coroutines that delay for a d
524511
fun doSomething() {
525512
// launch ten coroutines for a demo, each working for a different time
526513
repeat(10) { i ->
527-
launch {
514+
mainScope.launch {
528515
delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc
529516
println("Coroutine $i is done")
530517
}
@@ -544,21 +531,19 @@ of the activity no more messages are printed, even if we wait a little longer.
544531
<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
545532

546533
```kotlin
547-
import kotlin.coroutines.*
548534
import kotlinx.coroutines.*
549535

550-
class Activity : CoroutineScope by CoroutineScope(Dispatchers.Default) {
551-
536+
class Activity {
537+
private val mainScope = CoroutineScope(Dispatchers.Default) // use Default for test purposes
538+
552539
fun destroy() {
553-
cancel() // Extension on CoroutineScope
540+
mainScope.cancel()
554541
}
555-
// to be continued ...
556542

557-
// class Activity continues
558543
fun doSomething() {
559544
// launch ten coroutines for a demo, each working for a different time
560545
repeat(10) { i ->
561-
launch {
546+
mainScope.launch {
562547
delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc
563548
println("Coroutine $i is done")
564549
}
@@ -597,6 +582,9 @@ Destroying activity!
597582
As you can see, only the first two coroutines print a message and the others are cancelled
598583
by a single invocation of `job.cancel()` in `Activity.destroy()`.
599584

585+
> Note, that Android has first-party support for coroutine scope in all entities with the lifecycle.
586+
See [the corresponding documentation](https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope).
587+
600588
### Thread-local data
601589

602590
Sometimes it is convenient to have an ability to pass some thread-local data to or between coroutines.

kotlinx-coroutines-core/common/src/CoroutineScope.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import kotlin.coroutines.intrinsics.*
3131
*
3232
* ### Android usage
3333
*
34-
* Android has first-party support for coroutine scope in all entities with lifecycle.
34+
* Android has first-party support for coroutine scope in all entities with the lifecycle.
3535
* See [the corresponding documentation](https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope).
3636
*
3737
* ### Custom usage
@@ -50,7 +50,7 @@ import kotlin.coroutines.intrinsics.*
5050
*
5151
* /*
5252
* * Note: if this instance is destroyed or any of the launched coroutines
53-
* * in this method throw an exception, then all nested coroutines are cancelled.
53+
* * in this method throws an exception, then all nested coroutines are cancelled.
5454
* */
5555
* fun showSomeData() = scope.launch { // launched in the main thread
5656
* // ... here we can use suspending functions or coroutine builders with other dispatchers

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

+5-7
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,19 @@
55
// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit.
66
package kotlinx.coroutines.guide.exampleContext10
77

8-
import kotlin.coroutines.*
98
import kotlinx.coroutines.*
109

11-
class Activity : CoroutineScope by CoroutineScope(Dispatchers.Default) {
12-
10+
class Activity {
11+
private val mainScope = CoroutineScope(Dispatchers.Default) // use Default for test purposes
12+
1313
fun destroy() {
14-
cancel() // Extension on CoroutineScope
14+
mainScope.cancel()
1515
}
16-
// to be continued ...
1716

18-
// class Activity continues
1917
fun doSomething() {
2018
// launch ten coroutines for a demo, each working for a different time
2119
repeat(10) { i ->
22-
launch {
20+
mainScope.launch {
2321
delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc
2422
println("Coroutine $i is done")
2523
}

ui/coroutines-guide-ui.md

+22-59
Original file line numberDiff line numberDiff line change
@@ -408,47 +408,40 @@ during UI freeze.
408408
### Structured concurrency, lifecycle and coroutine parent-child hierarchy
409409

410410
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
413413
collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore.
414414

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`:
421423

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`:
425424
<!--- CLEAR -->
426425

427426
```kotlin
428-
abstract class ScopedAppActivity: AppCompatActivity(), CoroutineScope by MainScope() {
427+
class MainActivity : AppCompatActivity() {
428+
private val scope = MainScope()
429+
429430
override fun onDestroy() {
430431
super.onDestroy()
431-
cancel() // CoroutineScope.cancel
432+
scope.cancel()
432433
}
433-
}
434-
```
435-
436-
Now, an activity that is associated with a job has to extend ScopedAppActivity
437-
438-
```kotlin
439-
class MainActivity : ScopedAppActivity() {
440434

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
442436
// actual implementation
443437
}
444438

445439
suspend fun showIOData() {
446-
val deferred = async(Dispatchers.IO) {
447-
// impl
440+
val data = withContext(Dispatchers.IO) {
441+
// compute data in background thread
448442
}
449443
withContext(Dispatchers.Main) {
450-
val data = deferred.await()
451-
// Show data in UI
444+
// Show data in UI
452445
}
453446
}
454447
}
@@ -457,43 +450,14 @@ class MainActivity : ScopedAppActivity() {
457450
Every coroutine launched from within a `MainActivity` has its job as a parent and is immediately cancelled when
458451
activity is destroyed.
459452

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).
492455

493456
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
495458
when the parent job is cancelled. An example of that is shown in the
496459
["Children of a coroutine"](../docs/coroutine-context-and-dispatchers.md#children-of-a-coroutine) section of the guide to coroutines.
460+
497461
<!--- CLEAR -->
498462

499463
### Blocking operations
@@ -649,7 +613,6 @@ After delay
649613
[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html
650614
[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
651615
[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
653616
[withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html
654617
[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
655618
[CoroutineStart]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/index.html

0 commit comments

Comments
 (0)