Chapters

Hide chapters

Kotlin Coroutines by Tutorials

Second Edition · Android 10 · Kotlin 1.3 · Android Studio 3.5

Section I: Introduction to Coroutines

Section 1: 9 chapters
Show chapters Hide chapters

16. Android Concurrency Before Coroutines
Written by Nishant Srivastava

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

The importance of concurrency was discovered quite early on by people who started with Android development. Android is inherently asynchronous and event-driven, with strict requirements as to which threads certain things can happen. Add to this the often-cumbersome Java callback interfaces, and you will be trapped in spaghetti code pretty quickly (aptly termed as “Callback Hell”). No matter how many coding patterns you use to avoid it, you will encounter state changes across multiple threads in one way or the other.

The only way to create a responsive app is by leaving the UI thread as free as possible, letting all the hard work be done asynchronously by background threads.

Note: You’ve already met the term “Callback Hell” in the first chapter. It’s the situation in which you have to serially execute and process the results of asynchronous services by nesting callbacks, often several layers deep.

The purpose of coroutines is to take care of the complications of working with asynchronous programming. You write code sequentially, like you usually would, and then leave the hard asynchronous work up to coroutines.

Using coroutines in Android provides some of the following benefits:

  • Coroutines are a feature provided by a Kotlin library and, thus, can be updated independently from the Android platform releases.

  • Coroutines make asynchronous code look synchronous, making the code more readable. Also, since a synchronous sequence of steps is much easier to manage than asynchronous code, coroutines enable greater confidence in changing the flow when needed.

  • Thanks to coroutines, getting rid of any callbacks and the need to pass around state information is fairly easy, i.e., storing temporary state in a Presenter/ViewModel is simplified. State is not passed across multiple methods any longer.

  • Coroutines are a language feature provided out of the box by Kotlin and, thus, they can be updated independently from the Android platform releases.

  • Coroutines enable better, concise and testable code.

In this chapter, you’ll learn about what different mechanisms already exist for asynchronous programming on the Android platform and why coroutines are a much better replacement for all of them. You’ll see what Kotlin coroutines bring to the table and how they simplify various facets of Android development.

Getting started

Async Wars
Async Wars

For this chapter, you will use a basic app called Async Wars to learn about various async primitives in Android and coroutines at a high level. If you have already downloaded the starter project, then import it into Android Studio.

The project consists of some pre-written utility classes under the package utils. Let’s go over them one by one:

  1. DownloaderUtil: A singleton which has a method called downloadImage() that fetches an image from a pre-setup URL returning a Bitmap. This is done on the main thread and it will be your goal to execute this method on a background thread, and then you will display the image on the screen.
  2. ImageDownloadListener: Interface which is used as a listener for images being downloaded.
  3. BroadcasterUtil: A singleton which is used to abstract away the calls made using LocalBroadcastManager.
  4. MyBroadcastReceiver: Implementation of BroadcastReceiver class used as an adapter between the sender and an ImageDownloadListener.
  5. Extensions.kt: Utility Kotlin extension methods.

Under the package async, you will find GetImageAsyncTask and MyIntentService classes, which will be used and discussed at a later stage in this chapter.

Apart from that, there is MainActivity class where everything is wired up for making calls to download images using various async constructs in Android and to display them in the UI. Almost all the code is pre-written to make it easier for you to switch between these async constructs and see the results. There are two important sections inside MainActivity class that you should take note of:

  1. MethodToDownloadImage: This is an enum class defined inside the MainActivity class, which enumerates all the various types of async construct types in Android.
  2. Inside the onCreate() is a code region marked to be modified:
//region
val doProcessingOnUiThread = true
val methodToUse = MethodToDownloadImage.Thread
//endregion

This is where you will make the changes to trigger the right kind of async construct for downloading an image and displaying it in the UI. Here, when working with async constructs, you will have to set doProcessingOnUiThread = false. After that, the value of methodToUse, which will be one of the items from the MethodToDownloadImage enum class, will be used later to trigger the specific async method.

When not dealing with async constructs, simply set back to doProcessingOnUiThread = true.

Run the app. You will see a UI like below with a button and an animating spinner. The spinner is there to show the impact of calls on the UI thread while a widget is animating. The button will trigger a calculation of a Fibonacci sequence number on the main thread when the flag doProcessingOnUiThread is set to true.

Starter Project
Starter Project

Does Android need coroutines?

When you start an Android application, the first thread spawned by its process is the main thread, also known as the UI thread. This is the most important thread of an application. It is responsible for handling all the user interface logic, user interaction and also tying the application’s moving parts together.

UI blocking processing
EO kvajgevx mvoxaptabv

fun runUiBlockingProcessing() {
  // Processing
  showToast("Result: ${fibonacci(40)}")
}
// ----------- Helper Methods -----------//
fun fibonacci(number: Int): Long {
  return if (number == 1 || number == 2) {
    1
  } else fibonacci(number - 1) + fibonacci(number - 2)
}
UI blocking processing
EI gfukqihp ktudickuqn

Threads

A thread is an independent path of execution within a program. Every thread in Java is created and controlled by a java.lang.Thread instance. A Java program can have many threads, and these threads can run concurrently, either asynchronously or synchronously.

Sample usage

You can create a thread in two ways:

// Creation
class MyThread : Thread() {

  override fun run() {
    doSomeWork()
  }
}

// Usage
val thread = MyThread()
thread.start()
// Creation
class MyRunnable : Runnable {
  override fun run() {
    doSomeWork()
  }
}

  // Usage
val runnable = MyRunnable()
val thread = Thread(runnable)
thread.start()
​```kotlin
fun getImageUsingThread() {
// Download image
val thread = Thread(myRunnable)
thread.start()
}```
inner class MyRunnable : Runnable {
  override fun run() {
    // Download Image
    val bmp = DownloaderUtil.downloadImage()

    // Update UI on the UI/Main Thread with downloaded bitmap
    runOnUiThread {
      imageView?.setImageBitmap(bmp)
    }
  }
}
Download image using Thread
Miphmaek uraga oqils Sljauq

inner class MyRunnable : Runnable {
  override fun run() {
    // Download Image
    val bmp = DownloaderUtil.downloadImage()

    // Update UI on the UI/Main Thread with downloaded bitmap
    runOnUiThread {
      imageView?.setImageBitmap(bmp)
    }
  }
}
E/AndroidRuntime: FATAL EXCEPTION: Thread-4
    Process: com.raywenderlich.android.asyncwars, PID: 3127
    android.view.ViewRootImpl$CalledFromWrongThreadException: 
      Only the original thread that created a view hierarchy can touch its views.

AsyncTask

In Java, you usually put the code you want to run asynchronously into the run method of a class, which implements the Runnable interface. This works well if all you need to do is offload work to another thread. However, it becomes cumbersome when you need to relay the results of that thread back to the UI thread.

AsyncTask Process Flow
IyzyvVuwn Ffazuxf Nmok

Sample usage

class ExampleActivity : Activity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    MyTask().execute(url)
  }

  private inner class MyTask : AsyncTask<String, Void, String>() {

    override fun doInBackground(vararg params: String): String {
      val url = params[0]
      return doSomeWork(url)
    }

    override fun onPostExecute(result: String) {
      super.onPostExecute(result)
      // do something with result
    }
  }
}

To see a working example, in your MainActivity.kt file under the onCreate() function, set methodToUse = MethodToDownloadImage.AsyncTask.

fun getImageUsingAsyncTask() {
  // Download image
  val myAsyncTask = GetImageAsyncTask(imageDownloadListener)
  myAsyncTask.execute()
}
class GetImageAsyncTask(val imageDownloadListener: ImageDownloadListener) :
    AsyncTask<String, Void, Bitmap>() {


    // This executes on the background thread
    override fun doInBackground(vararg p0: String?): Bitmap? {
        // Download Image
        return DownloaderUtil.downloadImage()
    }

    // This executes on the UI thread
    override fun onPostExecute(bmp: Bitmap?) {
        super.onPostExecute(bmp)
        if (isCancelled) {
            return
        }

        // Pass it to the listener
        imageDownloadListener.onSuccess(bmp)

        // Cancel this async task after everything is done.
        cancel(false)
    }
}
private val imageDownloadListener = object : ImageDownloadListener {
  override fun onSuccess(bitmap: Bitmap?) {
    // Update UI with downloaded bitmap
    imageView?.setImageBitmap(bitmap)
  }
}
Download image using AsyncTask
Vacqlief ozelu odumb OkmmtKorn

Download image using AsyncTask
Qigdjeux ilejo iwihc ArkgvCuvp

Handlers

Handler is part of the HaMeR Framework (Handler, Message & Runnable), which is the recommended framework for communication between threads in Android. This is the one used, under the hood, by the AsyncTask class.

  object handler: Handler(){
    override fun handleMessage(msg: Message?) {
      // Consume the message
    }
  }
val runnable = Runnable {
    // update the ui from here
}

val handler = Handler(Looper.getMainLooper())
handler.post(runnable)
fun getImageUsingHandler() {
    // Create a Handler using the main Looper
    val uiHandler = Handler(Looper.getMainLooper())

    // Create a new thread
    Thread {
      // Download image
      val bmp = DownloaderUtil.downloadImage()

      // Using the uiHandler update the UI
      uiHandler.post {
        imageView?.setImageBitmap(bmp)
      }
    }.start()
  }
Download image using Handler
Tuztdieg aviga edasl Vefmpey

HandlerThreads

The UI thread already comes with a Looper and a MessageQueue. For other threads, you need to create the same objects if you want to leverage the HaMeR framework. You can do this by extending the Thread class as follows:

// Preparing a Thread for HaMeR
class MyLooperThread : Thread() {

  lateinit var handler: Handler

  override fun run() {
    // adding and preparing the Looper
    Looper.prepare()

    // the Handler instance will be associated with Thread’s Looper
    handler = object : Handler() {
      override fun handleMessage(msg: Message) {
        // process incoming messages here
        
      }
    }

    // Starting the message queue loop using the Looper
    Looper.loop()
  }
}
var handlerThread: HandlerThread? = null
fun getImageUsingHandlerThread() {
  // Download image
  // Create a HandlerThread
  handlerThread = HandlerThread("MyHandlerThread")

  handlerThread?.let{
    // Start the HandlerThread
    it.start()
    // Get the Looper
    val looper = it.looper
    // Create a Handler using the obtained Looper
    val handler = Handler(looper)
    // Execute the Handler
    handler.post {
      // Download Image
      val bmp = DownloaderUtil.downloadImage()

      // Send local broadcast with the bitmap as payload
      BroadcasterUtil.sendBitmap(applicationContext, bmp)
    }
  }
}

override fun onDestroy() {
  super.onDestroy()

  // Quit and cleanup any instance of dangling HandlerThread
  handlerThread?.quit()
}
HandlerThread
GozwhajSrnouy

Download image using HandlerThread
Bilwyeid itibo abuqp ZulnhihQppiab

Service

The definition of a component implies the existence of a container. You usually describe all your components to the container using some document; the container will create, suspend, resume and destroy components depending on the state of the application or on the available resources on the device.

Sample usage

class ExampleService : Service() {

  fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    doSomeLongProccesingWork()
    return START_NOT_STICKY
  }

  fun onBind(intent: Intent): IBinder? {
    return null
  }

  fun doSomeLongProccesingWork(){
    // Do some work
    // Stop service when required
    stopSelf()
  }
}

It is your responsibility to stop a Service when its work is completed by calling either the stopSelf() or the stopService() method. The Service doesn’t know what is going on in the code running in your thread or executor task — it is your responsibility to let it know when you’ve started and when you’ve finished.

Intent service

As stated previously, Service components, by default, are started in the main thread like any other Android component. If you need the service to run a task as a background task, then it’s up to you to create a separate thread and move your work to that thread. The Android frameworks also offers a sub-class of Service that can do all the threading work for you: IntentService.

Sample usage

// Required constructor with a name for the service
class MyIntentService : IntentService("MyIntentService") {

  override fun onHandleIntent(intent: Intent?) {
    //Perform your tasks here
    doSomeWork();
  }
}

To see a working example, in your MainActivity.kt file under the onCreate() function, set methodToUse = MethodToDownloadImage.IntentService. This makes sure that when the button is clicked, the method getImageUsingIntentService() is called. Here is the method definition:

fun getImageUsingIntentService() {
  // Download image
  val intent = Intent(this@MainActivity, MyIntentService::class.java)
  startService(intent)
}
// Required constructor with a name for the service
class MyIntentService : IntentService("MyIntentService") {

  override fun onHandleIntent(intent: Intent?) {
    // Download Image
    val bmp = DownloaderUtil.downloadImage()

    // Send local broadcast with the bitmap as payload
    BroadcasterUtil.sendBitmap(applicationContext, bmp)
  }
}
Download image using IntentService
Xadxnuus ubeyi agomj IkqinvSozcaji

Sending data from a Service to the UI

You learned that a started Service is an Android component that is not bound to the UI. If you need to send some data from a service to a different component, like an Activity, you need some other mechanisms like the LocalBroadcastManager that you used via the BroadcasterUtil in the previous example. You can see how to send data from a service in the onHandleIntent() method of the MyIntentService class:

override fun onHandleIntent(intent: Intent?) {
  // Download Image
  val bmp = DownloaderUtil.downloadImage()

  // Send local broadcast with the bitmap as payload
  BroadcasterUtil.sendBitmap(applicationContext, bmp)
}
/**
  * Send local broadcast with the bitmap as payload
  * @param context Context
  * @param bmp Bitmap
  * @return Unit
  */
fun sendBitmap(context: Context, bmp: Bitmap?) {
    val newIntent = Intent()
    bmp?.let {
        newIntent.putExtra("bitmap", it)
        newIntent.action = MainActivity.FILTER_ACTION_KEY
        LocalBroadcastManager.getInstance(context).sendBroadcast(newIntent)
    }
}
class MyBroadcastReceiver(val imageDownloadListener: ImageDownloadListener) : BroadcastReceiver() {
  override fun onReceive(context: Context, intent: Intent) {
      val bmp = intent.getParcelableExtra<Bitmap>("bitmap")

      // Pass it to the listener
      imageDownloadListener.onSuccess(bmp)
  }
}
/**
  * Register Local Broadcast Manager with the receiver
  * @param context Context
  * @param myBroadcastReceiver MyBroadcastReceiver
  * @return Unit
  */
fun registerReceiver(context: Context, myBroadcastReceiver: MyBroadcastReceiver?) {
    myBroadcastReceiver?.let {
        val intentFilter = IntentFilter()
        intentFilter.addAction(MainActivity.FILTER_ACTION_KEY)
        LocalBroadcastManager.getInstance(context).registerReceiver(it, intentFilter)
    }
}

/**
  * Unregister Local Broadcast Manager from the receiver
  * @param context Context
  * @param myBroadcastReceiver MyBroadcastReceiver
  * @return Unit
  */
fun unregisterReceiver(context: Context, myBroadcastReceiver: MyBroadcastReceiver?) {
    myBroadcastReceiver?.let {
        LocalBroadcastManager.getInstance(context).unregisterReceiver(it)
    }
} 
// ----------- Lifecycle Methods -----------//
override fun onStart() {
  super.onStart()
  BroadcasterUtil.registerReceiver(this, myReceiver)
}

override fun onStop() {
  super.onStop()
  BroadcasterUtil.unregisterReceiver(this, myReceiver)
}

Executors

You’ve seen that you can encapsulate code into a Runnable implementation in order to eventually run it in some given Thread. Every object that can execute what’s defined as a Runnable can be abstracted using the Executor interface, introduced in Java 5.0 as part of the concurrent APIs.

interface Executor {
    fun execute(command: Runnable)
}
interface Callable<T> {
    fun call(): T
}

Sample usage

val executor = Executors.newFixedThreadPool(4)
(1..10).forEach {
  executor.submit {
    print("[Iteration $it] Hello from Kotlin Coroutines! ")
    println("Thread: ${Thread.currentThread()}")
  }
}

To see a working example, in your MainActivity.kt file under the onCreate() function, set methodToUse = MethodToDownloadImage.Executor. This makes sure that, when you click the button, the method getImageUsingExecutors() is called. Here is the method definition:

fun getImageUsingExecutors() {
  // Download image
  val executor = Executors.newFixedThreadPool(4)
  executor.submit(myRunnable)
}
Download image using Executor
Zifmwuog imere erifk Esacoqib

WorkManager

Announced at Google I/O 2018 as part of Jetpack, WorkManager aims to simplify the developer experience by providing a first-class API for system-driven background processing. The WorkManager API makes it easy to specify deferrable, asynchronous tasks and when they should run. It is intended for background jobs that should run even if the app is no longer in the foreground. Where possible, it delegates its work to a JobScheduler, Firebase JobDispatcher, or Alarm Manager + Broadcast receivers depending on the Android version. If your app is in the foreground, it will even try to do the work directly in your process. The task is still guaranteed to run, even if your app is force-quit or the device is rebooted.

WorkManager Process Flow
JikzKenanex Gqiputf Cgoy

Sample usage

// A simple Worker
class DoSomeWorker : Worker() {
    // This method will run in background thread and WorkManger 
    // will take care of it
    override fun doWork() : WorkerRequest() { 
        doSomeWork()
        return WorkResult.SUCCESS
    } 
}

// Usage
// Create the request
val request : WorkRequest = OneTimeWorkRequestBuilder<DoSomeWorker>()
                             .build()
// Enqueue the request
val workManager : WorkManager = WorkManager.getInstance()
workManager.enqueue(request)

In short, the WorkManager is another library that is trying to solve the old problem of executing long-running jobs on the Android platform. It delegates the logic to different components that are available only on specific versions of the platform. If you decide to use this library, you accept all the fallbacks and workarounds used to enable support for older platforms/APIs. WorkManager is seen as the third attempt by Google to solve the job management problem on the Android Platform and will probably not be the last.

RxJava + RxAndroid

Reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change. The essence of reactive programming is the observer pattern.

Sample usage

Observable.just("Hello", "from", "RxJava")
        .subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(/* an Observer */);

This will execute the Observable on a new thread and emit results through onNext() on the main thread.

var single: Disposable? = null
fun getImageUsingRx() {
  // Download image
  single = Single.create<Bitmap> { emitter ->
    DownloaderUtil.downloadImage()?.let { bmp ->
      emitter.onSuccess(bmp)
    }
  }.observeOn(AndroidSchedulers.mainThread())
    .subscribeOn(Schedulers.io())
    .subscribe { bmp ->
      // Update UI with downloaded bitmap
      imageView?.setImageBitmap(bmp)
    }
}

override fun onDestroy() {
  super.onDestroy()

  // Cleanup disposable if it was created i.e. not null
  single?.dispose()
}
Download image using RxJava
Tepkseud uyodo arizf TfLuza

Download image using RxJava
Gemlzeel adica anuxx ZyTeyo

Coroutines

Now that you have a clear idea about various ways of doing asynchronous work in Android, as well as the pros and cons, let’s come back to Kotlin coroutines. Kotlin coroutines are a way of doing things asynchronously in a sequential manner. Creating coroutines is cheap versus creating threads.


dependencies {
  ..
  // Coroutines
  final def coroutineVer = "1.3.0"
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVer"
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVer"
}

GlobalScope.launch {
  // Download Image in background
  val deferredJob = async(Dispatchers.IO) {
    DownloaderUtil.downloadImage()
  }
  withContext(Dispatchers.Main) {
    val bmp = deferredJob.await()
    // Update UI with downloaded bitmap
    imageView?.setImageBitmap(bmp)
  }
}
Download image using Coroutine
Rumygaok ucona uhofc Dukuupefo

Introducing Anko

While Kotlin does remove much of the verbosity and complexity typically associated with Java, no programming language is perfect and, thus, libraries that build on top of the language are born. Anko is one such library that uses Kotlin and provides a lot of extension functions to make your Android development easier.

button.setOnClickListener {
  launch(UI){
    val userId = fetchUserString("user_id_1").await()
    val user = deserializeUser(userId).await()
    showUserData(user)
  }
}
button.onClick {
  val userId= bg { fetchUserString("user_id_1").await() }
  val user = bg { deserializeUser(userId).await() }
  showUserData(user)
}

Key points

Where to go from here?

Phew! That was a lot of background on asynchronous programming in Android! But the good thing is that you made it!

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now