Kotlin Migration from AsyncTask to Coroutines (KMAC)

In the Android world, long-running operations such as heavy computations, database operations, or network operations, are not expected to be executed on the UI or main thread because these operations do not complete immediately, and tend to block the thread from which they were called.

It would be a total disaster for the UI if this operation was performed on the main thread. In modern applications, there is a huge reliance on long-running operations to provide rich content to users, and as we know, maintaining a great UI experience is very attractive and keeps users coming back. To avoid UI jank and freezes, work must be moved off the main thread and onto other threads to avoid losing users.

The Android framework has a strict policy regarding this; by default, it does not allow long-running operations such as database interactions or network requests to be performed on the main thread. Exceptions are thrown at runtime, if this policy is violated.

Android originally designed the AsyncTask API to help with asynchronous tasks without requiring third-party libraries. I won’t go into the inner workings of AsyncTask because, as of API level 30, AsyncTask is deprecated and replaced by alternatives, which we’ll discuss in this post.

Why is AsyncTask deprecated?

  1. There are three main parts of this API that you should understand before working with this API:
  2. AsyncTask must be subclassed before you can use it.
  3. It comes with three general types, namely Params, Progress, and Result.
  4. There are four execution steps: onPreExecute, doInBackground, onProgressUpdate, onPostExecute.

What are Kotlin coroutines?

Coroutines are a concurrency approach where you can suspend and resume task execution without blocking the UI thread. Multiple coroutines can be executed on a single thread without blocking that thread. 

Kotlin Coroutines were developed from scratch in Kotlin by JetBrains to reduce complexity when dealing with asynchronous tasks and concurrency. One of its advantages is seen in the simplification of asynchronicity with sequential code.

To use Kotlin coroutines on Android, there are some basic components you need to understand.

Suspend functions

Suspend functions are similar to regular functions. The only difference is the suspend modifier on the function. At compile time, the compiler marks the function as a suspend point. Such functions can be suspended or resumed. The last point is that suspend functions can only be called from coroutines or other suspend functions.

The suspend function is defined like this:

suspend fun  invokeSuspendFunc (){ }

And a regular function is defined like this:

fun  invokeRegularFunc (){ }

Coroutine builders

Coroutine builders are used to launch new coroutines. Common builders are launch() and async() .

Coroutine context & dispatchers

This determines which thread the operation should be executed on.

Using Kotlin Coroutines for Asynchronous Tasks

To start coroutines, we will add some additional dependencies to launch coroutines from logical scopes in your application. The advantage of this is that when the scope in which the coroutine was launched is destroyed, the coroutine is automatically cancelled.

Add the following dependencies:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.3" 
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"


Case study

private  fun  coDoWork (binding: ActivityMainBinding) {
    lifecycleScope.launch(Dispatchers.IO) {
        println( "running task on ${Thread.currentThread().name}" )
         val result = factorial(input)
         val text = "Factorial of $result = $result"
        withContext(Dispatchers.Main) {
            println( "Accessing UI on ${Thread.currentThread().name}" )
            binding.displayText.text = text
        }
    }
    println( "running end on ${Thread.currentThread().name}" )
}

Examining the output of the above code block, the statement on line 11 is executed before the code in the coroutine block. We have been able to achieve asynchronicity with coroutines using sequential code, without blocking the main thread.

On line 2, we create a coroutine and mark its execution to be done on the worker thread. This frees up the main thread to perform other operations, such as the statement on line 11.

Since UI applications are single-threaded, Kotlin coroutines give us the flexibility to switch between threads to perform thread-specific operations; one example is accessing UI components. binding.displayText.*text* = text will throw an exception if accessed from a thread other than the main thread. To safely notify the UI of the results of the work that has been done, we switch to the main thread using withContext(Dispatchers.Main) {} .

Good luck!

Post a Comment

Previous Next

نموذج الاتصال