Asynchronous Apis Made easy (Kotlin code)

5 min read

Today we will be covering a small subset of the dozens of ways one can perform asynchronous processing in Android, along with some Apis provided by Java, and known third party libraries.

AsyncTask

class AsyncTaskExample : AsyncTask<String, String, String>() {

    companion object {
        private val TAG = AsyncTaskExample::class.java.simpleName
    }

    override fun onPreExecute() {
        super.onPreExecute()
        // should print main - this runs on the main thread.
        // Gets called before the doInBackground method does
        Log.d(TAG, "onPreExecute: " + Thread.currentThread().name)
    }

    override fun doInBackground(vararg strings: String): String {
        // should print 'AsyncTask #{number}' - this runs in a background thread
        Log.d(TAG, "doInBackground: " + Thread.currentThread().name)
        return strings[0] /* Return anything for this example, such as test1*/
    }

    override fun onPostExecute(s: String) {
        super.onPostExecute(s)
        // s should be equal to whatever is returned in the doInBackground method,
        // in this case test1
        Log.d(TAG, "onPostExecute() called with: s = [$s]")

        // Should print main - this runs on the main thread.
        // Gets called after the doInBackground method does
        Log.d(TAG, "onPostExecute: " + Thread.currentThread().name)
    }
}

To execute the AsyncTask, the following is done:

AsyncTaskExample().execute("test1", "test2", "test3")

A list of strings is passed in as required by the type.

The execute() method is part of the AsyncTask class.

For those newbies learning to use the AsyncTask, and for those oldies who forgot how to use it, this is a very simple/straight forward example of us performing a background task on a list of strings.

The onPreExecute runs on its callers thread, that is the thread that called execute(), and is called before the doInBackground method is called and it runs a background thread.

The doInBackground returns some result which is then received in the onPostExecute, which runs in the main thread.

Thread

    private fun runThreadExample() {
        val thread = Thread({
            // The code that runs in here will be running in a separate thread
            Log.d(TAG, "runThreadExample(): ${Thread.currentThread().name}")
        })

        thread.start()
    }

The code is very straight forward and self explanatory. A thread value is created, with a runnable passed in. The code inside the runnable gets called on a separate thread.

Handler

    private fun runHandlerExample() {
        val handlerThread = HandlerThread("handlerThreadName")
        handlerThread.start() // you must first start the handler thread before using it

        val handler = Handler(handlerThread.looper)

        handler.post({
            // runs on background thread with the name provided above (e.g 'handlerThreadName')
            Log.d(TAG, Thread.currentThread().name)
        })
    }

Handler usage is just as easy as Thread. A handler runs on a handler thread. The HandlerThread is a direct sub class of Thread (extends it) that takes in a thread name, and/or a priority using one of its two constructor methods. Just like the name suggests, this type of thread is for Handlers. The HandlerThread provides a looper which is used by its handler to to run a piece of code in the looper specified. The Handler uses this looper to determine what thread to run on (Note if no looper is passed in, it will just run on the current threads looper – in other words it will just run in the same thread it was called from).

Timer

    private fun runTimerExample() {
        val timer = Timer() // can also be used to schedule tasks at an interval

        timer.schedule(object : TimerTask() { // Schedules the specified task for execution after the specified delay.
            override fun run() {
                // Runs on background thread
                Log.d(TAG, "runTimerExample(): ${Thread.currentThread().name}")

                timer.cancel() // Terminates this timer, discarding any currently scheduled tasks.
                timer.purge() // Removes all cancelled tasks from this timer's task queue.
            }
        }, 0) // delay in milliseconds
    }

Another easy threading example, the Timer class is a facility for threads to schedule tasks for future execution in a background thread. Tasks may be scheduled for one-time execution, or for repeated execution at regular intervals

RxJava

Time to step it up a noch.

    private fun runRxJavaExample() {
        // For this example lets use a string array and rx maps as the 
        // methods used to run on separate threads and log the thread names out
        Observable.fromArray(arrayOf("test1", "test2"))
                .map {
                    // Runs on scheduler/background thread
                    Log.d(TAG, "First (background) map function: ${Thread.currentThread().name}")
                }
                .map {
                    // Runs on scheduler/background thread
                    Log.d(TAG, "Second (background) map function: ${Thread.currentThread().name}")
                }
                // anything before this runs on the Schedulers.io thread
                .subscribeOn(Schedulers.io())
                // anything after this runs on the Schedulers.computation() thread
                .observeOn(Schedulers.computation())
                .map {
                    // Runs on computation/background thread
                    Log.d(TAG, "Third (Computation) map function: ${Thread.currentThread().name}")
                }
                // anything after this runs on the AndroidSchedulers.mainThread()
                .observeOn(AndroidSchedulers.mainThread())
                .map {
                    // Runs on UI thread
                    Log.d(TAG, "Fourth (UI/main) map function: ${Thread.currentThread().name}")
                }
                .subscribe({
                    // Runs on UI/main thread
                    Log.d(TAG, "runRxJavaExample().subscribe: ${Thread.currentThread().name}")
                })
    }

The comments are very clear but there is something that we need to play closer attention to. Take a look at the subscribeOn and the observeOn calls. By carefully analyzing what is going we, we can see that subscribeOn runs every call that was made before it was called on the specified thread, and the observeOn calls everything that was called after in the specified thread. What happens if we call subscribeOn AFTER an observeOn call?

Lets take a look at this code and what gets printed out

    private fun runRxJavaExample2() {
        Observable.fromArray(arrayOf("test1", "test2"))
                .observeOn(AndroidSchedulers.mainThread())
                .map {
                    Log.d(TAG, "Fourth (UI/main) map function: ${Thread.currentThread().name}")
                }
                .subscribeOn(Schedulers.io())
                .subscribe({
                    Log.d(TAG, "runRxJavaExample().subscribe: ${Thread.currentThread().name}")
                })
    }

This prints the following:

E/MainActivity: Map function: main
E/MainActivity: runRxJavaExample2().subscribe: main

From looking at the log we can see that the SubscribeOn has no effect after an observeOn has been called. This is very important to keep in mind, when i started with RxJava, i made this exact same mistake and being an Rx noobie, took me sometime to figure out. The reason for this is because

…the SubscribeOn operator designates which thread the Observable will begin operating on, no matter at what point in the chain of operators that operator is called. ObserveOn, on the other hand, affects the thread that the Observable will use below where that operator appears. For this reason, you may call ObserveOn multiple times at various points during the chain of Observable operators in order to change on which threads certain of those operators operate.

Theres a great article talking about this more in depth here.

Future

Introduced in Java 5, a Future

represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation.

But this class has been improved a couple of times such as in Googles Guava library as a ListenableFuture, and also CompletableFutures (Java 8). Listenable and Completable futures are futures that allow the users to chain calls similar to RxJava and its operators.

Here is an example of what a normal Future (Java 5) implementation looks like

class FuturesExample {

    private val executorService = Executors.newFixedThreadPool(1)

    companion object {
        private val TAG = FuturesExample::class.java.simpleName
    }

    fun execute() {
        // Runs a task on a background thread. For running multiple task,
        // you can use executorService.invokeAll() which takes a array of callable's
        val future = executorService.submit {
            // runs in background pool thread
            Log.d(TAG, "execute(): " + Thread.currentThread().name)
        }

        // As long as the task is still happening, do not ove on
        while (!future.isDone) {
            // wait 1 second
            SystemClock.sleep(TimeUnit.SECONDS.toMillis(1))
        }

        // We are now done, time to shut down
        executorService.shutdown()
    }
}

To run the future we just do

FuturesExample().execute()

CompletableFutures example:

A CompletableFuture future is a Future that may be explicitly completed (setting its value and status), and may be used as a CompletionStage, supporting dependent functions and actions that trigger upon its completion.

class CompletableFuturesExample {

    companion object {
        private val TAG = CompletableFuturesExample::class.java.simpleName
    }

    fun execute() {
        val executorService = Executors.newSingleThreadExecutor()

        // create a supplier object to start with
        val supplier = Supplier { "Hello World!" }

        // This function takes some text and returns its length
        val function = Function<String, Int> { text -> text.length }

        // requires api 24+ (N)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            CompletableFuture
                    .supplyAsync(supplier, executorService) // provide the initial supplier and specify executor
                    .thenApply(function) // do something
                    .whenComplete { int, _ ->
                        Log.d(TAG, "$int") // returns the length of 'Hello World!'

                        executorService.shutdown() // complete
                    }
        }
    }

}

and same as the future, to use it we just call the execute() method

    private fun runFuturesExample() {
        CompletableFuturesExample().execute()
    }

Very similar to RxJava isn’t it? Kinda fun to play around with actually.

For the curious ones, there is a nice guide on CompletableFutures here.

More:

There are other asynchronous APIs you can play with such as Java 9’s Reactive Streams, IntentServices, and Loaders, Job Schedulers, GcmNetWorkManager, Android Job (one of my favorites), Sync adapters, and many more.

Complete source coed can be found here.

Thank you for reading, Thats all for now!