- vừa được xem lúc

Hiểu sâu hơn về Kotlin Coroutines

0 0 36

Người đăng: Chu Đình Tùng

Theo Viblo Asia

Hôm nay, mình sẽ trình bày về Kotlin Coroutines trong lập trình Android giải quyết khó khăn trong việc xử lý bất đồng bộ(asynchronous)

I. Trước tiên chúng ta cùng tìm hiểu về Asynchronous Programing (Lập trình bất đồng bộ)

👉️ Trong Android được chia làm 2 luồng chính là Main Thread (UI Thread) và Background Thread.
👉️👉️ Main Thread có nhiệm vụ xử lý tác vụ liên quan đến UI như hiển thị hình ảnh chữ viết,... Tuy nhiên nếu các bạn xử lý các tác vụ nặng tốn thời gian, tài nguyên như đọc ghi files, request network, tính toán,... trên UI Thread thì sẽ gặp hiện tượng ANR, nguyên nhân là Main thread phải đợi xử lý xong tác vụ tiêu tốn thời gian xong mới thực hiện tác vụ tiếp theo -> Cần 1 luồng khác gọi là Background Thread xử lý và cập nhật kết quả trên UI Thread.
img1
📢📢📢 Vẫn có các phương pháp khác như RX, Thread + Callbacks/Asynctask/Handler. Tuy nhiên cái giá phải trả khi sử dụng Thread là khá đắt. Tại sao -> link đây.
Tuy nhiên trong bài viết này chỉ đề cập đến Kotlin Coroutine thôi nhé 😅😅😅.

II. Kotlin Coroutines

  • Coroutines có nghĩa là kết hợp công việc với nhau
    img2
  • Coroutines cơ bản là hiểu nó như một light-weight threed nhưng nó không phải là thread, nó chỉ hoạt động như thread. Hàng ngàn coroutines có thể bắt đầu cùng một lúc, nhưng nếu hàng nghìn thread chạy một lúc thì performance sẽ oẳng.
  • Cấu trúc của đoạn Code sử dụng Coroutines
    img3
fun fetchData(){
// launch a coroutine CoroutineScope(Dispatchers.Main).launch { val result = getListString() // hàm getListString() này được chạy bất đồng bộ // thế nhưng cách viết code lại giống như đang viết code đồng bộ (code từ trên xuống) }
} suspend fun getListString(): List<String> { // makes a request and suspends the coroutine return suspendCoroutine { apiServie.getListString() }
}

Nhìn đoạn code trên tuy chạy bất đồng bộ nhưng nhìn cách viết như tuần tự ✌️

III. Bây giờ đến phần chính. Tìm hiểu các thành phần của Coroutines.

3.1: Suspend Function:

  • Là function có thể start, pause, resume. Cụ thể hơn là nó có thể dừng trên main thread nhưng có thể tiếp tục trên bất kỳ luồng nào khác (Worker Thread) để thực thi tiếp tác vụ tại thời điểm nó dừng. Cùng xem hình ảnh so sánh giữa Blocking và Suspend để thấy được sự khác biệt.

Đây là hình ảnh Blocking

img4

Đây là hình ảnh dùng suspend function

img5

3.2: Coroutine Builder: launch (), async()

  • launch(): trả về 1 đối tượng Job để có thể cancel coroutines.
  • async(): return kết quả trả về. Nó trả về 1 object Deffered<T>, cần sử dụng đến await() để lấy kết quả trả về.
    Ảnh minh họa

img6

  • Ngoài ra: withContext(): giống như async nhưng không cần phải gọi await().
    👍️👍️👍️👍️👍️👍️Note rules: Tại cùng một thời điểm:
    🍌 Sử dụng withContext() khi KHÔNG CẦN các suspend function chạy song song.
    🍌 Sử dụng withContext() để trả về MỘT single task.
    🍌 Sử dụng async khi CẦN các suspend function chạy song song.
    🍌 Sử dụng async() để trả về NHIỀU task sông song - multiple task.

Để chứng minh cho sự bốc phét của mình thì đây là log 🤣🤣🤣

🥒 Với việc dùng async thì thời gian chạy 2 suspend function trên là 5015 milis dù mình đã để delay mỗi function 5000.
img7
img8
🥒 Với việc dùng withContext() thì kết quả nhận được như thế này.
img9
img10
😇😇😇 Wow thời gian gấp đôi hơn 10077 milis. Đây là lý do tại sao nói nên sử dụng withContext() khi làm việc SINGLE Task

3.3: CoroutineContext: là tập hợp các phần tử định nghĩa behavior của coroutines, các tham số khi khởi tạo CoroutineScope gồm:

  • Job: kiểm soát vòng đời của Coroutine.
  • CoroutineDispatcher: xác định thread nào mà coroutines chạy trện nó.
  • CoroutineName: định nghĩa tên của coroutine dùng để Debug.
  • CoroutineExceptionHandler: xử lý uncaught exception.

3.4: CoroutineScope: Có nhiệm vụ theo dõi tất cả coroutine được tạo thông qua nó. từ đó có thể cancel coroutines bằng cách gọi scope.cancel(). Một vài scope phổ biến:

  • GlobalScope: không được khuyến khích sử dụng vì nó sẽ không tự hủy nếu chúng ta gọi job.cancel() nếu job này bao bọc nó ở trong.
  • viewModelScope.
  • lifecycleScope.

3.5: CoroutineDispatcher: Xác định thread mà coroutines sẽ chạy, gồm:

  • Dispatcher.IO: sử dụng cho tác vụ I/O: đọc ghi file, network, etc...
  • Dispatcher.Default: sử dụng cho tác vụ tính toán nặng tiêu tốn CPU.
  • Dispatcher.Main: chạy Main Thread.
  • Dispatcher.Unconfied: chạy trên Thread hiện tại cho đến khi gặp 1 suspend function thì nó sẽ chạy trên Woker Thread.

3.6: Những điều cần lưu ý khi Cancel coroutine:

  • Coroutines sẽ không dừng ngay lập tức khi cancel mà nó vẫn tiếp tục chạy cho đến khi hoàn thành công việc. Cần sử dụng đến isActive() hoặc ensureActive() để check. Với withContext() hoặc delay có thể cancel được lên không cần check.
  • Hoặc có thể dùng try/catch.
  • 📢📢📢📢📢 Một lưu ý nhỏ: Coroutines bị cancel thì không suspend được nữa, nếu muốn thực hiện tác vụ suspend bên trong một finally block cần thêm coroutineContext là NonCancellable.
 val job = launch { try { work() } catch (e: CancellationException){ println(“Work cancelled!”) } finally { withContext(NonCancellable){ delay(1000L) // or some other suspend fun println(“Cleanup done!”) } }
}

3.7: Xử lý Exception trong Coroutines:

img11

Khi một coroutines throw ra exception, exception sẽ đẩy lên cho parent của nó xử lý. khi đó:

👉️👉️ 1. Parent sẽ cancel toàn bộ coroutine con của nó.

👉️👉️ 2. Parent sẽ cancel chính nó.

👉️👉️ 3. Exception sẽ được đẩy lên cao hơn.

=> Để coroutine nằm trong scope bị exception không bị cancel thì sử dụng SupervisorJob().

  • SupervisorJob() : khi dùng thì mô tả trên không xảy ra nên coroutine gây ra exception đó phải dùng try catch hoặc CoroutineContext cần có CoroutineExceptionHandler nếu không có thể sẽ crash app.
  • CoroutineExceptionHandler: là 1 thành phần của CoroutineContext để bắt Exception. Exception được bắt:
    👉️👉️ KHI: Exception bị throw bởi 1 coroutines tự động throw exception (launch thay vì async).
    👉️👉️ TẠI: Handler được khai báo ở root CoroutineScope hoặc root coroutine.
  • Lưu ý: Trong trường hợp tiếp theo exception sẽ không được bắt nếu CoroutineExceptionHandler không được khai báo ở root coroutine hoặc root scope. Để làm được điều này cần khai báo như sau
val scope = CoroutineScope(Job())
scope.launch(handler) { launch { throw Exception("Failed coroutine") }
} // TH ko được CoroutineExceptionHandler caught.
val scope = CoroutineScope(Job())
scope.launch { launch(handler) { throw Exception("Failed coroutine") }
} // Thử 2 cách ở dưới đây.
val scope = CoroutineScope(Job())
scope.launch(handler) { launch { throw Exception("Failed coroutine") }
} // Hoặc
val scope = CoroutineScope(Job() + handler)
scope.launch { launch { throw Exception("Failed coroutine") }
}

IV. Tổng kết: trên đây là toàn bộ những kiến thức về Kotlin Coroutines mà mình tìm hiểu cũng như sưu tầm ở nhiều nguồn. Rất mong các bạn VOTE để mình có động lực viết các bài tiếp theo. Xin chào và hẹn gặp lại!👋

V. Link tài liệu tham khảo:

https://kotlinlang.org/docs/async-programming.html

https://viblo.asia/s/cung-hoc-kotlin-coroutine-z45bxjBoZxY

Tài liệu của 1 A zai trên github mà mình lâu không vào quên link, mong a zai thông cảm. 😂😂😂

Bình luận

Bài viết tương tự

- vừa được xem lúc

Kotlin dễ ẹc - Lớp vs đối tượng: Object expression và Object declaration

Khi nào dùng. . Khi muốn tạo một đối tượng với những sự thay đổi nhỏ của lớp mà không phải khai báo tường minh lớp con của lớp đó. .

0 0 37

- vừa được xem lúc

Làm đoạn code Kotlin của bạn dễ đọc hơn với 5 Extensions này.

Bạn có thể đã xem qua nhiều bài viết về Kotlin Extension, nhưng bài viết này không phải là tất cả về Extension. Đó là về việc làm cho code của bạn trở nên dễ đọc hơn, vì vậy tôi đã tập trung vào việc giải thích và đưa ra các tiện ích mở rộng hàng đầu của tôi để làm code bạn tự nhiên nhất có thể.

0 0 46

- vừa được xem lúc

Phòng tránh NullPointerException trong Android Kotlin

I. Dẫn nhập. . Như chúng ta đã biết một trong những điểm nổi trội của Kotlin so với Java đó là khả năng xử lý null vô cùng hiệu quả.

0 0 50

- vừa được xem lúc

KOTLIN - 10 MẸO VÀ THỦ THUẬT LÀM VIỆC VỚI LIST

Giới thiệu. List (danh sách) là một Collection trong Kotlin.

0 0 52

- vừa được xem lúc

Navigation Component

Introduction. Navigation Component are in simple terms, components required to perform navigations and Navigation refers to the interactions that allow users to navigate across various areas within th

0 0 34

- vừa được xem lúc

Flutter Vs Kotlin Multiplatform Mobile (KMM) Vs React Native

Trong phát triển đa nền tảng hiện nay chúng ta đang có nhiều tùy chọn như Flutter, React Native và bây giờ là Kotlin Multiplatform Mobile. Bây giờ mối quan tâm là chúng ta phải chọn công nghệ nào tron

0 0 107