Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@Mercandj
Last active February 2, 2022 23:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mercandj/18d5c4b6fb4531e8c6ae724d6fae554a to your computer and use it in GitHub Desktop.
Save Mercandj/18d5c4b6fb4531e8c6ae724d6fae554a to your computer and use it in GitHub Desktop.
android_asynchronous

Android: Asynchronous

Note that by default, on Android, everything is on the main thread (also called, the UI thread). In order to do heavy task or network calls, you must be on another thread to avoid to block the UI thread.

Once your work done on non-main-thread, you will need to come back on the main thread in order to update the UI.

Keep in mind that every Activity, View or Fragment will crash if UI is modified on a non-main-thread.

  • This document is to highlight some way to work with asynchronous on Android.
  • Not vocation to be exhaustive and explicit
  • The goal is for you to pick the good one.

PLAN

    1. Setup
    1. Come back on main thread
    1. Thread
    1. WorkerThread
    1. Coroutine
    1. Library specific
    1. Conclusion

0. Setup

All following technics will use this setup: methods and interface.

interface Listener {

   @MainThread
   fun onCompleted()
}

@MainThread
fun startWorkAsynchronous(listener: Listener) {
   // Will be fill in following sections
   // the goal will be to call `listener.onCompleted()` once completed on the mainThread.
}

@WorkerThread
private fun startWorkSynchronous() {
   // TODO with your eavy computation
}

1. Come back on main thread

val mainHandler = Handler(Looper.getMainLooper())
mainHandler.post {
   // You are here on the main thread
}

2. Thread

Easier way to have an asynchronous method to do eavy job:

private val mainHandler by lazy { Handler(Looper.getMainLooper()) }

@MainThread
fun startWorkAsynchronous(listener: Listener) {
   val thread = Thread(Runnable {
      startWorkSynchronous()
      mainHandler.post(Runnable {
         listener.onCompleted()
      })
   }).start()
}

Attention:

  • With thread implemented like that, each call to startWorkAsynchronous(Listener) will create a new thread.
  • Often, a best practice will be to avoid to create multple times thread with someting like if (doningAsyncJob) { return; }

3. WorkerThread

private val handlerThread by lazy { createHandlerThread() }
private val handler by lazy { Handler(handlerThread.looper) }
private val mainHandler by lazy { Handler(Looper.getMainLooper()) }

@MainThread
fun startWorkAsynchronous(listener: Listener) {
   handler.post(Runnable {
      startWorkSynchronous()
      mainHandler.post(Runnable {
         listener.onCompleted()
      })
   })
}

private fun createHandlerThread(): HandlerThread {
    val handlerThread = HandlerThread("example-thread-name")
    handlerThread.start()
    return handlerThread
}

4. Coroutine

Often too complicated if your only goal is to do that (you will import too much extra deps for nothing).

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7")
@MainThread
fun startWorkAsynchronous(listener: Listener) {
   GlobalScope.launch(Dispatchers.Default) {
      startWorkSynchronous()
      CoroutineScope.launch(Dispatchers.Main) {
         listener.onCompleted()
      }
   }
}

PS: Even if suspend is part of Kotlin language, try not spread it accross your project.

5. Library specific

5. a. OkHttp & Retrofit

With "Square" libraries that handle network call, there are 2 possibilities to make call.

One synchronous, that should not be done on the main thread (use one of the above method to call it).

val okHttpResponse = okHttpClient.newCall(okHttpRequestBuilder.build()).execute()

Otherwise, you can use the library to handle the asynchronism like that

okHttpClient.newCall(okHttpRequestBuilder.build()).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        TODO("Not yet implemented")
    }

    override fun onResponse(call: Call, response: Response) {
        TODO("Not yet implemented")
    }
})

6. Conclusion

A lot of methods exist to make asynchronous code. Each have PROs and CONs depending of your usecase.

Try to avoid well known "callback hell" in your code once you will need to chain multiple asynchronous tasks. A good approach, is to write @WorkerThread synchronous method and use this method from a non-main-thread to have only one callback when the "chain" is complete.


Other articles and projects on Mercandj.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment