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

Android asynchronous tasks với suspend function, suspendcoroutine và suspendcancellablecoroutine

0 0 113

Người đăng: Dang Anh Quan

Theo Viblo Asia

Giới thiệu

Làm việc với asynchronous tasks là việc rất hay gặp khi dev android và đi cùng với sự phát triển của android đã có rất nhiều công cụ hỗ trợ cho chúng ta như Thread, Handler, AsyncTask, RxJava và gần nhất là Kotlin Corotuines.

Nói về curoutines thì một từ khoá được nhắc đến rất nhiều là suspend, chúng ta sẽ cùng đi tìm hiểu về sử dụng suspend để làm asynchronous tasks trong android nhé.

suspend

Có thể bạn đã đọc ở đâu đó từ khoá suspend cho phép function có khả năng pause và resume, hay một cách khác là suspend function sẽ chờ cho đến khi kết quả sẵn sàn để return và trong thời gian suspend thì nó ko block thread.

Nếu tìm hiểu sâu hơn thì có thể bạn biết một suspend function sẽ được tự động thêm param Continuation để thành một state machine.

Nhưng các cách giải thích trên kia có vẻ không dễ hiểu lắm với người mới bắt đầu nên chúng ta hãy thử cách khác nhé. Trước hết hãy cùng nhìn lại quá khứ chút, trước đây để thực hiện asynchronous tasks chúng ta có gì nào

  • AsyncTask: sử dụng callback với các function doInBackground, onPreExecute, onPostExecute, onProgressUpdate. Vấn đề của asynctask là callback hell, nghĩa là khi bạn muốn lồng các async task với nhau thì nó trở nên rất khó quản lý, maintain, code phụ thuộc khó mở rộng ,...

  • RxJava: Xuất hiện và thay thế callback hell của AsyncTask, tuy nhiên cấu trúc vẫn còn rất rườm rà và cồng kềnh.

  • Coroutines: light weight thread, cú pháp ngắn gọn, code asynchronous tasks theo style đồng bộ cực kỳ dễ hiểu. Thread được chỉ định bởi các hàm suspend tránh việc task bị gọi sai thread gây exception.

Về cơ bản thì các asynchronous tasks là task lâu và chúng ta không biết khi nào nó sẽ kết thúc và thường dùng callback để thông báo hoặc observable. Trên một góc độ nào đó ta có thể hiểu Coroutines làm giúp cho chúng ta việc viết callback, chúng ta chỉ cần await task return success hay là fail. Coroutines làm bằng cách sử dụng suspend, về bản chất là interface Continuation.

Trong Android hiện tại có 2 lib phổ biến là retrofit (network) và room (db) đã tích hợp suspend giúp cho chúng ta gọi api hay db rất gọn gàng. Nhưng liệu đó đã là tất cả, xin trả lời là không, sức mạnh của coroutines còn nhiều hơn và rất nhiều case khác chúng ta có thể xử dụng nó và các bạn có thể đọc thêm ở đây https://github.com/LukasLechnerDev/Kotlin-Coroutine-Use-Cases-on-Android

Ngoài use case ở ví dụ trên thì android còn rất nhiều case cần asynchronous tasks khác, ví dụ khi các bạn thực hiện code với view như khi thực hiện liên tiếp các animation.

Ví dụ 1: Thực hiện animation A, sau đó animation B. Cái này thì animation lib có hỗ trợ sẵn rồi

Ví dụ 2: Thực hiện animation A, sau đó thực hiện recycler view scroll đến item x, sau đó thực hiện animation B

  • Cách 1: Nghe animation A kết thúc, gọi recyclerview sctoll to x ở callback animation end của A. Lắng nghe recyclerview scroll tới x rồi gọi start animation B.

Có vẻ cũng ổn nhỉ, không đâu nếu bạn muốn mở rộng như sau, sau animation A thì gọi api get user và thực hiện recycler view scroll đến item x, cả 2 cùng kết thúc mới thực hiện animation B.

Việc implement theo cách 1 khiến cho các CR liên quan asynchronous tasks rất khó thực hiện và tiềm ẩn rủi ro phát sinh bug rất lớn.

  • Cách 2: Sử dụng suspendcoroutinesuspendcancellablecoroutine (nên dùng)

suspendcoroutine và suspendcancellablecoroutine

suspendcoroutinesuspendcancellablecoroutine giúp chúng ta thực hiện việc code asynchronous tasks một cách tường mình, dễ hiểu, hạn chế phụ thuộc để tằng khả năng mở rộng và maintian.

  • suspendcancellablecoroutine returns một CancellableContinuation để chúng ta resume, resumeWithException và throws CancellationException nếu continuation bị cancelled.

  • suspendCoroutine thì hơi khác là không bị cancelled bởi Job.cancel()

suspend fun Animator.awaitEnd() = suspendCancellableCoroutine<Unit> { cont -> // Add an invokeOnCancellation listener. If the coroutine is // cancelled, cancel the animation too that will notify // listener's onAnimationCancel() function cont.invokeOnCancellation { cancel() } addListener(object : AnimatorListenerAdapter() { private var endedSuccessfully = true override fun onAnimationCancel(animation: Animator) { // Animator has been cancelled, so flip the success flag endedSuccessfully = false } override fun onAnimationEnd(animation: Animator) { // Make sure we remove the listener so we don't keep // leak the coroutine continuation animation.removeListener(this) if (cont.isActive) { // If the coroutine is still active... if (endedSuccessfully) { // ...and the Animator ended successfully, resume the coroutine cont.resume(Unit) } else { // ...and the Animator was cancelled, cancel the coroutine too cont.cancel() } } } })
}

src https://gist.githubusercontent.com/chrisbanes/2173c0bad46cdf534396bfb83f02884a/raw/f388ef046f823c2ba03c607ecb28795ba6c1c0cd/code.kt

hoặc


suspend fun RecyclerView.awaitScrollEnd() { // If a smooth scroll has just been started, it won't actually start until the next // animation frame, so we'll await that first awaitAnimationFrame() // Now we can check if we're actually idle. If so, return now if (scrollState == RecyclerView.SCROLL_STATE_IDLE) return suspendCancellableCoroutine<Unit> { continuation -> continuation.invokeOnCancellation { // If the coroutine is cancelled, remove the scroll listener recyclerView.removeOnScrollListener(this) // We could also stop the scroll here if desired } addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { // Make sure we remove the listener so we don't leak the // coroutine continuation recyclerView.removeOnScrollListener(this) // Finally, resume the coroutine continuation.resume(Unit) } } }) }
}

src: https://gist.githubusercontent.com/chrisbanes/005d31f1d6091165c28922454ae35d6a/raw/de1a47a81e8a3f1b2ba27798eaa6563494274f69/code.kt

Với suspendCancellableCoroutine thì tư tưởng code khi chúng ta làm các asynchronous tasks là làm cho hành động được gói gọn, async tasks ko biết khi kết thúc thì sẽ làm gì tiếp theo, giúp cho code của chúng độc lập nhất, dễ dàng maintain và mở rộng sau này. Nói một cách khác với callback thì chúng ta điều hướng hành động tiếp theo ngay trong đoạn code liên quan asynchronous tasks. Còn với suspend chúng ta chỉ lắng nghe điều kiện asynchronous tasks còn việc hành động tiếp như thế nào sẽ do đoạn code bên ngoài thực thi quyết định.

Kết

Với việc sử dụng suspendCancellableCoroutine và suspendcoroutine chúng ta có thể làm các asynchronous tasks trở nên tường minh, giảm sự phụ thuộc nên dễ bảo trì và mở rộng code sau này.

Tham khảo

https://medium.com/androiddevelopers/suspending-over-views-19de9ebd7020

https://medium.com/androiddevelopers/suspending-over-views-example-260ce3dc9100

Bình luận

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

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

Sử dụng bất đồng bộ với Flow API trong Kotlin Coroutines

Giới thiệu. Trong khi xây dựng ứng dụng để xử lý các hoạt động bất đồng bộ chúng ta thường nghĩ ngay tới AsynchTask, RxJava.

0 0 177

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

Simplifying APIs with coroutines and Flow

Bài viết này trình bày cách đơn giản hóa các API sử dụng coroutines và Flow cũng như cách tạo bộ điều hợp của riêng bạn bằng cách sử dụng các API pauseCancellableCoroutine và callbackFlow. Đối với những người thích đi sâu vào bên trong các quy trình, những API đó sẽ được mổ xẻ và bạn sẽ thấy chúng h

0 0 43

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

Đưa work xuống background với Coroutines trong Android

Giới thiệu. Coroutines giờ đã không còn xa lạ với dev android nữa, nhưng liệu chúng ta đã dùng đc hết các công dụng nó mang lại ngoài việc dùng để gọi api với retrofit hay truy cập db với room, nhiều

0 0 34

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

Kotlin Coroutines trong Android

Mở đầu. Bài viết này dành cho bất cứ ai tò mò về Coroutines trong Kotlin nhưng không biết chính xác nó là gì. . Chính xác thì Coroutines là gì.

0 0 71

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

Dispatchers trong Kotlin Coroutines

Dispatcher là cái gì. Có 4 loại Dispatchers:. . Dispatchers.

0 0 25

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

Kotlin Coroutines cheat sheet nâng cao dành cho Android Engineer

Sau khi làm việc với Kotlin Coroutines một thời gian, có thể anh em đã quen với các khái niệm cơ bản như suspend function và các hàm launch, async..., có thể giải quyết các use case đơn giản một cách

0 0 8