androidkotlinfuturejava.util.concurrentcompletable-future

Using Future blocks UI in application


In my Android app I want to use java.util.concurrent tools to make operations that return value (for educational purposes). I read that CompletableFuture can be used for this. I made a project where I simulate an operation that takes some time to execute:

class MyRepository {

    private var counter = 0

    // Here we make a blocking call
    fun makeRequest(): Int {
        Thread.sleep(2000L)
        counter += 1
        return counter
    }
}

Then I call this function from UI thread:

fun getNumber(): Int {
    val completableFuture = CompletableFuture<Int>()
    Executors.newCachedThreadPool().submit<Any?> {
        val result = myRepository.makeRequest()
        completableFuture.complete(result)
        null
    }
    return completableFuture.get()
}

The problem is that UI is blocked while this operation is being executed. How can I use CompletableFuture to make operation like this without blocking UI of application?

I want to use only java.util.concurrent tools for this.


Solution

  • To do asynchronous work using exclusively java.util.concurrent, you should use an ExecutorService. And to return the value from your function you need to use a callback. And if you're working with UI, you need to provide a way to cancel work so callbacks won't try to work with your UI after its gone.

    I think conventional behavior is to fire the callback on the Looper of the thread that called this function, or on the main looper as a fallback if it was called from a non-Looper thread.

    private val executor = Executors.newFixedThreadPool(10)
    
    // Call in onDestroy of UI
    fun cancel() {
        executor.shutdownNow()
    }
    
    fun getNumberThread(callback: (Int)->Unit) {
        val looper = Looper.myLooper ?: Looper.mainLooper
        val handler = Handler(looper)
        val myCallable = MyCallable(myRepository)
        executor.execute {
            try {
                val result = myCallable.call()
                if (Thread.currentThread().isInterrupted()) return
                handler.post { callback(result) }
            } catch {e: MyException) {
                if (Thread.currentThread().isInterrupted()) return
                handler.post { callback(-1) } // if that's how you want to return error results
            }
        }
    }