0% found this document useful (0 votes)
7 views82 pages

Kotlin Coroutines Labs

The document provides a comprehensive guide on Kotlin Coroutines, covering topics such as creating coroutines, coroutine context, dispatchers, exception handling, and the use of async and await. It includes practical examples and code snippets for better understanding. The document also discusses coroutine scopes and the implications of cancellation and supervision in coroutines.

Uploaded by

Minhh Maii
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views82 pages

Kotlin Coroutines Labs

The document provides a comprehensive guide on Kotlin Coroutines, covering topics such as creating coroutines, coroutine context, dispatchers, exception handling, and the use of async and await. It includes practical examples and code snippets for better understanding. The document also discusses coroutine scopes and the implications of cancellation and supervision in coroutines.

Uploaded by

Minhh Maii
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 82

Kotlin

Coroutines Labs

1
Nội dung
◼ Bài 1. Xây dựng Coroutine đầu tiên
◼ Bài 2. Coroutine Context
◼ 2.1. Dispachers
◼ 2.2. withContext
◼ 2.3. Job
◼ 2.4. Time outs
◼ 3. Async và Await
◼ 4. CoroutineScope
◼ 5. Xử lý Exception và Supervision trong Coroutine
◼ 6. Sequence trong Kotlin
◼ 7. Giới thiệu về Flow trong Kotlin Coroutines 2
Bước 1

◼ Tạo Project Kotlin coroutine example


◼ Thêm các dependencies vào
app/build.gradle.kt
implementation("org.jetbrains.kotlinx:kotlinx-
coroutines-android:1.3.9")

3
Bước 2.

◼ Tạo package firstcoroutines, tạo file


BuildFirstCoroutines.kt trong package này.
◼ Tạo hàm main.
◼ Ấn nút mũi tên xanh và chạy Run (hoặc ấn
Ctrl+Shift+F10).

4
Chương trình coroutine đầu tiên
package
vn.edu.hust.soict.gv.quangnh.coroutineexample.firstcoroutines

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

fun main() {
GlobalScope.launch {
delay(1000)
print("Hello ")
}
print("World ")
Thread.sleep(2000)
} 5
Kết quả

6
Ví dụ 2

◼ Tạo coroutine dùng runBlocking: tạo ra


coroutine và block thread hiện tại
fun main() { a. Xác định kết quả của
runBlocking { chương trình này
delay(1000)
println("Hello ")
b. Hãy hiển thị tên tiến
delay(1000) trình của từng đoạn code
println("World ") trong chương trình
}
println("After runBlocking")
}
println(“Current Thread: ${Thread.currentThread().name}")
7
Kết quả

8
Bài 2. Coroutine Context
2.1. Dispatchers
◼ Dispatchers: quyết định Thread mà Coroutine chạy

◼ Dispatchers.Default
◼ Dispatchers.IO: đọc Database, Networking
◼ Dispatchers.Main: update UI
◼ Dispatchers.Unconfined

9
Dispatcher. Ví dụ 3
◼ Tạo package coroutinecontext
◼ Tạo file TestDispatchers:

package
vn.edu.hust.soict.gv.quangnh.coroutineexample.coroutinecontext

import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import
vn.edu.hust.soict.gv.quangnh.coroutineexample.MainActivity
11
object TestDispatchers {
fun runMyFirstCoroutines() {
GlobalScope.launch(Dispatchers.Default) {
Log.d(MainActivity::class.java.simpleName, "Dispatchers Default run
on ${Thread.currentThread().name}")
}
GlobalScope.launch(Dispatchers.IO) {
Log.d(MainActivity::class.java.simpleName, "Dispatchers IO run on
${Thread.currentThread().name}")
}
GlobalScope.launch(Dispatchers.Unconfined) {
Log.d(MainActivity::class.java.simpleName, "Dispatchers Unconfined
run on ${Thread.currentThread().name}")
}
GlobalScope.launch(Dispatchers.Main) {
Log.d(MainActivity::class.java.simpleName, "Dispatchers Main run on
${Thread.currentThread().name}")
}
}
}
12
File MainActivity.kt
package vn.edu.hust.soict.gv.quangnh.coroutineexample

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import
vn.edu.hust.soict.gv.quangnh.coroutineexample.coroutineconte
xt.TestDispatchers

class MainActivity : AppCompatActivity() {


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
TestDispatchers.runMyFirstCoroutines()
}
} 13
Kết quả khi chạy app

14
Nhận xét

◼ Các luồng chạy bất đồng bộ, không theo


đúng thứ tự code
◼ Dispachers Unconfined và Main đều chạy
trên Main thread. Tuy nhiên nếu Dispachers
Unconfined chạy quá lâu thì sẽ được chuyển
sang Thread mới.

15
Ví dụ 4
object TestDispachers {
fun runMyFirstCoroutines() {
GlobalScope.launch(Dispatchers.Unconfined) {
Log.d(MainActivity::class.java.simpleName, "Before delay -
Dispachers Unconfined run on ${Thread.currentThread().name}")
delay(1000)
Log.d(MainActivity::class.java.simpleName, "Dispachers
Unconfined run on ${Thread.currentThread().name}")
}
GlobalScope.launch(Dispatchers.Main) {
Log.d(MainActivity::class.java.simpleName, "Dispachers Main run
on ${Thread.currentThread().name}")
}
}
}

16
Kết quả

17
fun testMySecondWithContext() {
2.2. withContext
GlobalScope.launch(Dispatchers.IO) { Ví dụ 5
// Run long time task
Log.d("myLog", "Run long time task - Thread:
${Thread.currentThread().name}")
delay(2000)
withContext(Dispatchers.Main) {
// Update UI here
Log.d("myLog", "Update UI - Thread:
${Thread.currentThread().name}")
}
}
} 21
Kết quả

22
2.3. Job. Ví dụ 6
package
vn.edu.hust.soict.gv.quangnh.coroutineexample.coroutinecontext

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

23
fun main() {
val job1: Job = GlobalScope.launch {
delay(2000)
println("Hello Kotlin")
}

val job2: Job = GlobalScope.launch {


// job2 chờ đợi công việc của job1 hoàn thành rồi mới thực hiện
job1.join()
delay(1000)
println("I'm Coroutine")
}
Thread.sleep(4000)
}

24
Kết quả

25
Cancel coroutine. Ví dụ 7
fun main() {
runBlocking {
val job = launch(Dispatchers.Default) {
repeat(1000) {
delay(500)
println("I'm sleeping $it ...")
}
}
delay(1500)
job.cancel()
print("Cancelled coroutines")
}
}
26
Kết quả

27
Hàm cancelAndJoin(). Ví dụ 8
fun main() {
runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 5) { // computation loop, just waste CPU
// print a message twice a second
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500
}
}
}
delay(1400) // delay a bit
println("main: I'm tired of waiting")
job.cancelAndJoin() // cancel the job and waits for its completion
println("main: Now I can quit")
} 28
Kết quả

29
Biến isActive. Ví dụ 9
fun main() {
runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (isActive) { // computation loop, just waste CPU
// print a message twice a second
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500
}
}
}
delay(1400) // delay a bit
println("main: I'm tired of waiting")
job.cancelAndJoin() // cancel the job and waits for its completion
println("main: Now I can quit")
} 30
Kết quả

31
Coroutine với try … catch … finally
fun main() {
runBlocking {
val job = launch { Ví dụ 10
try {
repeat(1000) {
delay(100)
println("Hello Coroutine")
}
} finally {
println("Print from finally")
}

}
delay(300)
println("I want stop coroutine")
job.cancel()
}
} 32
Kết quả

33
fun main() { Hàm delay trong
khối finally
runBlocking {
val job = launch {

Ví dụ 11
try {
repeat(1000) {
delay(100)
println("Hello Coroutine")
}
} finally {
println("Print from finally")
delay(100)
println("Please print me last times")
}

}
delay(300)
println("I want stop coroutine")
job.cancel()
}
} 34
Kết quả

◼ Lý do: Hàm delay sẽ check coroutine còn


alive hay không, do đó hàm delay và các câu
lệnh sau đó không còn chạy
35
fun main() {
runBlocking {
val job = launch {
Hàm
try { withContext(NonCancellable)
Ví dụ 12
repeat(1000) {
delay(100)
println("Hello Coroutine")
}
} finally {
println("Print from finally")
withContext(NonCancellable) {
repeat(2) {
delay(100)
println("Print from NonCancellable")
}
}
}

}
delay(300)
println("I want stop coroutine")
job.cancel()
}
} 36
Kết quả

◼ Nhận xét: khối lệnh bên trong


withContext(NonCancellable) sẽ luôn được
thực hiện. 37
2.4. Timeouts. Ví dụ 13
fun main() {
runBlocking {
withTimeout(1800) {
repeat(1000) {
println("I'm sleeping $it")
delay(500)

}
}
}
}
Nghĩa là coroutine này chỉ chạy tối đa 1800 ms.

38
Kết quả

39
Xử lý Exception bằng withTimeoutOrNull
Ví dụ 14
fun main() {
runBlocking {
val result = withTimeoutOrNull(1800) {
repeat(1000) {
println("I'm sleeping $it")
delay(500)
}
"Done"
}
println("Result = $result")
}
}

40
Kết quả

41
Nếu thời gian chạy coroutine ít hơn thời
gian Timeout. Ví dụ 15

fun main() {
runBlocking {
val result = withTimeoutOrNull(1800) {
repeat(2) {
println("I'm sleeping $it")
delay(500)
}
"Done"
}
println("Result = $result")
}
}
42
Kết quả

43
3. Async và Await. Ví dụ 16

package
vn.edu.hust.soict.gv.quangnh.coroutineexample.async_await

import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlin.system.measureTimeMillis

44
fun main() {
runBlocking {
val time = measureTimeMillis {
val a = doSomethingFunny1()
val b = doSomethingFunny2()
println("a + b = ${a + b}")
}
println("Time = $time")
}
}
suspend fun doSomethingFunny1(): Int {
delay(1000)
return 10
}

suspend fun doSomethingFunny2(): Int {


delay(1000)
return 20
}

45
Kết quả

◼ Như vậy thời gian chạy cần đến 2101 ms (vì


là chạy tuần tự).
◼ Có cách khác để chạy nhanh hơn, đó là sử
dụng async – await.
46
Ví dụ 17: async - await

import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlin.system.measureTimeMillis

47
fun main() {
runBlocking {
val time = measureTimeMillis {
val a: Deferred<Int> = async { doSomethingFunny1() }
val b: Deferred<Int> = async { doSomethingFunny2() }
println(a.await() + b.await())
}
println("Time = $time")
}
}
suspend fun doSomethingFunny1(): Int {
delay(1000)
return 10
}

suspend fun doSomethingFunny2(): Int {


delay(1000)
return 20
}
48
Kết quả

49
4. CoroutineScope

◼ Nhận xét: cả
runBlocking, launch và
async đều chạy trong
một CoroutineScope
50
fun main() {
runBlocking { Ví dụ 18
val job1 = launch {
launch {
delay(100)
println("coroutine 1: Hello")
delay(1000)
println("coroutine 1: Goodbye")
}
launch {
delay(100)
println("coroutine 2: Hello")
delay(1000)
println("coroutine 2: Goodbye")
}
}
delay(500)
job1.cancel()

}
51
}
Kết quả

◼ Nhận xét: coroutine cha bị cancel thì


coroutine con cũng bị hủy theo.
◼ Nếu tác vụ nhất thiết phải hoàn thành kể cả
khi coroutine cha bị hủy thì dùng
GlobalScope. 52
fun main() {
runBlocking {
Ví dụ 19
val job1 = launch {
launch {
delay(100)
println("coroutine 1: Hello")
delay(1000)
println("coroutine 1: Goodbye")
}
launch {
delay(100)
println("coroutine 2: Hello")
delay(1000)
println("coroutine 2: Goodbye")
}
GlobalScope.launch { delay(500)
delay(100) job1.cancel()
println("coroutine 3: Hello") delay(2500)
delay(1000)
println("coroutine 3: Goodbye") }
} }
} 53
Kết quả

54
Ví dụ 20
fun main() {
runBlocking {
val job = launch {
repeat(3) {
delay(100)
println("coroutine: $it")
}
println("Print from parent")
}
job.join()
delay(1000)
}
}
55
Kết quả

56
Ví dụ 21
fun main() {
runBlocking {
val job = launch {
repeat(3) {
launch {
delay(100)
println("coroutine: $it")
}
}
println("Print from parent")
}
job.join()
delay(1000)
}
}
57
Kết quả

58
5. Xử lý Exception và
Supervision trong Coroutine
Ví dụ 22
fun main() {
runBlocking {
val job = GlobalScope.launch {
println("Throw Exception from Launch")
throw NullPointerException()
}
// chờ đợi coroutine hoàn thành
job.join()
val deferred = GlobalScope.async {
println("Throw Exception from Async")
throw IndexOutOfBoundsException()
}
}
} 59
Kết quả

◼ Lý do async không tạo ra các thông báo lỗi


vì các thông báo lỗi này đã được bắt bởi
biến deferred.

60
Khi thêm câu lệnh await()
fun main() {
Ví dụ 23
runBlocking {
val job = GlobalScope.launch {
println("Throw Exception from Launch")
throw NullPointerException()
}
// chờ đợi coroutine hoàn thành
job.join()
val deferred = GlobalScope.async {
println("Throw Exception from Async")
throw IndexOutOfBoundsException()
}
deferred.await()
}
} 61
Kết quả

62
fun main() {
runBlocking {
val job = GlobalScope.launch {
try {
println("Throw Exception from Launch")
throw NullPointerException()
} catch (e: NullPointerException) { Xử lý lỗi trong
println(e.toString())
} coroutine dùng
}
// chờ đợi coroutine hoàn thành
try … catch
job.join() Ví dụ 24
val deferred = GlobalScope.async {
println("Throw Exception from Async")
throw IndexOutOfBoundsException()
}
try {
deferred.await()
} catch (e: IndexOutOfBoundsException) {
println(e.toString())
63
}
Kết quả

64
fun main() {
runBlocking {
val handler = CoroutineExceptionHandler { _, exception ->
println("Error here: ${exception.toString()}")
}
val job = GlobalScope.launch(handler) {
println("Throw Exception from Launch")
throw NullPointerException()
}
// chờ đợi coroutine hoàn thành Bắt lỗi với
job.join()
val deferred = GlobalScope.async {
CoroutineExcep
println("Throw Exception from Async") tionHandler
}
throw IndexOutOfBoundsException()
Ví dụ 25
try {
deferred.await()
}catch (e: IndexOutOfBoundsException) {
println(e.toString())
}
} 65
}
Kết quả

66
Bắt lỗi + chỉ định context
val job = GlobalScope.launch(handler + Dispatchers.Default) {
println("Throw Exception from Launch")
throw NullPointerException()
}

67
CoroutineExceptionHandler không bắt được lỗi với async
Ví dụ 26
fun main() {
runBlocking {
val handler = CoroutineExceptionHandler { _, exception ->
println("Error here: ${exception.toString()}")
}
val job = GlobalScope.launch(handler + Dispatchers.Default) {
println("Throw Exception from Launch")
throw NullPointerException()
}
// chờ đợi coroutine hoàn thành
job.join()
val deferred = GlobalScope.async(handler) {
println("Throw Exception from Async")
throw IndexOutOfBoundsException()
}
deferred.await()
}
} 68
Kết quả

◼ Tổng kết:
◼ Sử dụng CoroutineExceptionHandler để bắt lỗi
với coroutine tạo ra bằng launch.
◼ Sử dụng try … catch để bắt lỗi với coroutine tạo
ra bằng async.
69
Nếu trong Coroutine cha có nhiều coroutine con, và các
coroutine con có khả năng tạo ra các lỗi. Ví dụ 27

◼ Khi Coroutine thứ 2 throw Exception thì các


coroutine khác sẽ dừng.
fun main() {
runBlocking {
val handle = CoroutineExceptionHandler {_, exception ->
println("Exception: $exception")
}
val job = GlobalScope.launch(handle) {
launch {
println("Coroutine 1")
delay(300)
println("Coroutine 1 continue")
throw IndexOutOfBoundsException("Coroutine 1")
} 70
launch {
println("Coroutine 2")
delay(200)
throw NullPointerException("Coroutine 2")
}
launch {
println("Coroutine 3")
delay(400)
println("Coroutine 3 continue")
throw ArithmeticException("Coroutine 3")
}
}
job.join()
delay(1000)
} // end of runBlocking
}
71
Kết quả

72
Bắt lỗi với suppressed. Ví dụ 28
fun main() {
runBlocking {
val handle = CoroutineExceptionHandler {_, exception ->
println("Exception: $exception with suppressed
${exception.suppressed.contentToString()}")
}
val job = GlobalScope.launch(handle) {
launch {
println("Coroutine 1")
delay(300)
println("Coroutine 1 continue")
throw IndexOutOfBoundsException("Coroutine 1")
}
73
launch {
try {
delay(Long.MAX_VALUE)
} finally {
throw ArithmeticException("Coroutine 2")
}
}

launch {
println("Coroutine 3")
delay(400)
println("Coroutine 3 continue")
throw ArithmeticException("Coroutine 3")
}
}
job.join()
delay(1000)
} // end of runBlocking
}
74
Kết quả

◼ Exception aggregation: When multiple children of a


coroutine fail with an exception, the general rule is
"the first exception wins", so the first exception
gets handled.
◼ All additional exceptions that happen after the first
one are attached to the first exception as
suppressed ones. 75
SupervisorJob và SupervisorScope
Ví dụ 29
fun main() {
runBlocking {
val supervisorJob = SupervisorJob()
with(CoroutineScope(coroutineContext +
supervisorJob)) {
val firstChild = launch {
println("Print from First Child")
throw NullPointerException()
}

76
val secondChild = launch {
firstChild.join()
println("print from second Child. First Child is
Active: ${firstChild.isActive}")
try {
delay(1000)
} finally {
println("Second Child Cancelled")
}
}
firstChild.join()
println("Cancelling SupervisorJob")
supervisorJob.cancel()
secondChild.join()
}
}
} 77
Kết quả

◼ Nhận xét: secondChild vẫn chạy ngay cả khi


firstChild đã throw Exception
78
fun main() {
runBlocking {
SupervisorScope
supervisorScope {
val firstChild = launch {
Ví dụ 30
println("Print from First Child")
throw NullPointerException()
}
val secondChild = launch {
firstChild.join()
println("print from second Child. First Child is Active:
${firstChild.isActive}")
try {
delay(1000)
} finally {
println("Second Child Cancelled")
}
}
firstChild.join()
secondChild.join()
}
} 79
Kết quả

80
6. Sequence trong Kotlin
Ví dụ 31
fun foo(n: Int) : Sequence<Int> = sequence {
for (i in 0..n) {
if (i % 2 == 0)
yield(i)
}
}

fun main() {
foo(10).forEach {
println(it)
}
}

81
Kết quả

82
Kết hợp sequence với map
fun main() {
foo(10).map{it * it}.forEach {
println(it)
}
}

◼ Kết quả

83
Kết hợp sequence với filter
fun main() {
foo(10).filter { it < 8 }.forEach {
println(it)
}
}

◼ Kết quả

84
7. Giới thiệu về Flow trong Kotlin Coroutines
Ví dụ 32
fun main() {
runBlocking {
val foo = foo(200)
foo(5).collect {
println("i = $it")
}
}
}

fun foo(n : Int): Flow<Int> = flow {


for(i in 0..n) {
delay(1000)
emit(i)
}
} 85
Kết quả

◼ Nhận xét:
◼ Flow chỉ cấp dữ liệu khi cần, do đó kết
quả được in ra mà không cần đợi 200
giây do lệnh val foo = foo(200).
◼ Flow chạy bất đồng bộ nên không ảnh
hưởng đến Thread hiện tại.
86

You might also like