@@ -168,7 +168,7 @@ You can find this code in `src/contributors/Contributors.kt`.
168
168
* The ` loadContributors() ` function is responsible for choosing how the contributors are loaded.
169
169
* The ` updateResults() ` function updates the UI. As a result, it must always be called from the UI thread.
170
170
171
- #### Task
171
+ #### Task 8
172
172
173
173
To familiarize yourself with the task domain, complete the following task.
174
174
@@ -191,7 +191,7 @@ After implementing this task, the resulting list for the "Kotlin" organization s
191
191
>
192
192
{type="note"}
193
193
194
- #### Solution {initial-collapse-state="collapsed"}
194
+ #### Solution 8 {initial-collapse-state="collapsed"}
195
195
196
196
Group users by their login. You can use
197
197
[ ` groupBy() ` ] ( https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/group-by.html ) that returns a map from
@@ -211,7 +211,7 @@ An alternative is to use
211
211
the [ ` groupingBy ` ] ( https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/grouping-by.html ) function instead
212
212
of ` groupBy ` .
213
213
214
- ## Using callbacks
214
+ ## Callbacks
215
215
216
216
The previous solution works, but blocks the thread and therefore freezes the UI. A traditional approach to avoiding this
217
217
is to use callbacks.
@@ -264,12 +264,12 @@ the main UI thread(AWT event dispatching thread).
264
264
However, if you try to load the contributors via the ` BACKGROUND ` option, you can see that the list is updated, but
265
265
nothing changes.
266
266
267
- #### Task
267
+ #### Task 1
268
268
269
269
Fix the ` loadContributorsBackground() ` in ` src/tasks/Request2Background.kt ` so that the resulting list was shown in the
270
270
UI.
271
271
272
- #### Solution {initial-collapse-state="collapsed"}
272
+ #### Solution 1 {initial-collapse-state="collapsed"}
273
273
274
274
Now the contributors are loaded, as you can see in the log, but the result isn't displayed. To fix this, call
275
275
the ` updateResults ` on the resulting list of users:
@@ -334,11 +334,11 @@ option, you can see that nothing is shown. The tests that immediately return the
334
334
335
335
Think about why the given code doesn't work as expected and try to fix it or check the solution below.
336
336
337
- #### Task (optional)
337
+ #### Task 2 (optional)
338
338
339
339
Rewrite the code so that the loaded list of contributors is shown.
340
340
341
- #### Solution (first attempt) {initial-collapse-state="collapsed"}
341
+ #### Solution 2 (first attempt) {initial-collapse-state="collapsed"}
342
342
343
343
Now many requests are started concurrently, which decreases the total loading time. However, the result isn't loaded.
344
344
The ` updateResults ` callback is called right after all the loading requests are started, so the ` allUsers ` list is not
@@ -366,7 +366,7 @@ for ((index, repo) in repos.withIndex()) { // #1
366
366
367
367
However, this code is also incorrect. Try to find an answer yourself or check the solution below.
368
368
369
- #### Solution (second attempt) {initial-collapse-state="collapsed"}
369
+ #### Solution 2 (second attempt) {initial-collapse-state="collapsed"}
370
370
371
371
Since the loading requests are started concurrently, no one guarantees that the result for the last one comes last. The
372
372
results can come in any order.
@@ -399,7 +399,7 @@ for (repo in repos) {
399
399
>
400
400
type={"note"}
401
401
402
- #### Solution (third attempt) {initial-collapse-state="collapsed"}
402
+ #### Solution 2 (third attempt) {initial-collapse-state="collapsed"}
403
403
404
404
An even better solution is to use the ` CountDownLatch ` class. It stores a counter initialized with the number of
405
405
repositories. This counter is decremented after processing each repository. It then waits until the latch is counted
@@ -468,7 +468,7 @@ interface GitHubService {
468
468
}
469
469
```
470
470
471
- #### Task
471
+ #### Task 3
472
472
473
473
The task is to change the code of the function loading contributors to make use of the new suspending functions.
474
474
@@ -482,7 +482,7 @@ Modify so that the new suspending functions are used instead of ones returning `
482
482
Run the program by choosing the ` SUSPEND ` option and ensure that the UI is still responsive while the GitHub requests
483
483
are performed.
484
484
485
- #### Solution {initial-collapse-state="collapsed"}
485
+ #### Solution 3 {initial-collapse-state="collapsed"}
486
486
487
487
Replace ` .getOrgReposCall(req.org).execute() ` with ` .getOrgRepos(req.org) ` and repeat the same replacement for the
488
488
second "contributors" request:
@@ -667,12 +667,12 @@ can be sent before the result for the previous one is received:
667
667
The total loading time is approximately the same as in the ` CALLBACKS ` version, but it doesn't need any callbacks.
668
668
What's more, ` async ` explicitly emphasizes which parts run concurrently in the code.
669
669
670
- #### Task
670
+ #### Task 4
671
671
672
672
In the ` Request5Concurrent.kt ` file, implement a ` loadContributorsConcurrent() ` function. For that, use the
673
673
previous ` loadContributorsSuspend() ` function.
674
674
675
- #### Tip {initial-collapse-state="collapsed"}
675
+ #### Tip 4 {initial-collapse-state="collapsed"}
676
676
677
677
As you'll see later, you can only start a new coroutine inside a coroutine scope. Copy the content
678
678
from ` loadContributorsSuspend() ` to the ` coroutineScope ` call, so that you can call ` async ` functions there:
@@ -696,7 +696,7 @@ val deferreds: List<Deferred<List<User>>> = repos.map { repo ->
696
696
deferreds.awaitAll() // List<List<User>>
697
697
```
698
698
699
- #### Solution {initial-collapse-state="collapsed"}
699
+ #### Solution 4 {initial-collapse-state="collapsed"}
700
700
701
701
Wrap each "contributors" request with ` async ` to create as many coroutines as the number of repositories. ` async `
702
702
returns ` Deferred<List<User>> ` .
@@ -1086,7 +1086,7 @@ call `withContext`, which is a `suspend` function inside the corresponding lambd
1086
1086
` updateResults ` callback takes an additional Boolean parameter as an argument saying whether all the loading completed
1087
1087
and the results are final.
1088
1088
1089
- #### Task
1089
+ #### Task 5
1090
1090
1091
1091
In the ` Request6Progress.kt ` file, implement the ` loadContributorsProgress() ` function that shows the intermediate
1092
1092
progress. Base it on the ` loadContributorsSuspend ` function from ` Request4Suspend.kt ` .
@@ -1099,7 +1099,7 @@ Use a simple version without concurrency; you'll add it later in the next sectio
1099
1099
>
1100
1100
{type="note"}
1101
1101
1102
- #### Solution {initial-collapse-state="collapsed"}
1102
+ #### Solution 5 {initial-collapse-state="collapsed"}
1103
1103
1104
1104
To store the intermediate list of loaded contributors in the "aggregated" state, define an ` allUsers ` variable which
1105
1105
stores the list of users, and then update it after contributors for each new repository are loaded:
@@ -1278,15 +1278,15 @@ To get a better understanding, watch the following video:
1278
1278
1279
1279
![ Video explaining the channels sample] ( https://youtu.be/HpWQUoVURWQ )
1280
1280
1281
- #### Task
1281
+ #### Task 6
1282
1282
1283
1283
Implement the function ` loadContributorsChannels() ` that requests all the GitHub contributors concurrently but shows
1284
1284
intermediate progress at the same time.
1285
1285
1286
1286
For this, use previous functions, ` loadContributorsConcurrent() ` from ` Request5Concurrent.kt `
1287
1287
and ` loadContributorsProgress() ` from ` Request6Progress.kt ` .
1288
1288
1289
- #### Tip {initial-collapse-state="collapsed"}
1289
+ #### Tip 6 {initial-collapse-state="collapsed"}
1290
1290
1291
1291
Different coroutines that concurrently receive contributor lists for different repositories can send all the received
1292
1292
results to the same channel:
@@ -1313,7 +1313,7 @@ repeat(repos.size) {
1313
1313
1314
1314
Since the ` receive() ` calls are sequential, no additional synchronization is needed.
1315
1315
1316
- #### Solution {initial-collapse-state="collapsed"}
1316
+ #### Solution 6 {initial-collapse-state="collapsed"}
1317
1317
1318
1318
As with the ` loadContributorsProgress() ` function, you can create an ` allUsers ` variable to store the intermediate
1319
1319
states of the "all contributors" list.
@@ -1504,7 +1504,7 @@ compileTestKotlin {
1504
1504
1505
1505
In the project corresponding to this tutorial it's already been added to the Gradle script.
1506
1506
1507
- #### Task
1507
+ #### Task 7
1508
1508
1509
1509
Refactor all the following tests in ` tests/tasks/ ` to use virtual time instead of real-time:
1510
1510
@@ -1517,7 +1517,7 @@ Request7ChannelsKtTest.kt
1517
1517
1518
1518
Compare the total running time with the time before this refactoring.
1519
1519
1520
- #### Tip {initial-collapse-state="collapsed"}
1520
+ #### Tip 7 {initial-collapse-state="collapsed"}
1521
1521
1522
1522
Replace ` runBlocking ` invocation with ` runBlockingTest ` , and
1523
1523
` System.currentTimeMillis() ` with ` currentTime ` :
@@ -1535,7 +1535,7 @@ fun test() = runBlockingTest {
1535
1535
Uncomment the assertions checking the exact virtual time.
1536
1536
Don't forget to add ` @UseExperimental(ExperimentalCoroutinesApi::class) ` .
1537
1537
1538
- #### Solution {initial-collapse-state="collapsed"}
1538
+ #### Solution 7 {initial-collapse-state="collapsed"}
1539
1539
1540
1540
Here's the solution for the concurrent case:
1541
1541
0 commit comments