0

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.

2
  • 1
    FutureTask.get() is a blocking call, which is why the UI freezes during its execution. Also, why not use coroutines? Commented Oct 14, 2023 at 20:17
  • Just for educational purpose. FutureTask has no other methods to return value. So how can I make non-blocking call with java.util.concurrent tools? Commented Oct 14, 2023 at 20:21

2 Answers 2

1

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
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

You could use a ListenableFuture, that way you can attach a completion listener instead of using a blocking get() call. https://developer.android.com/guide/background/asynchronous/listenablefuture

If you must only use java.util.concurrent tools, I suppose you could poll isDone() of the FutureTask using a Handler or similar.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.