Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin Coroutines - Are nested coroutines the proper way to handle different threading within one coroutine?

I'm trying out coroutines instead of RxJava on basic network calls for the fist time to see what it's like and running into some issues with lag/threading

In the below code, I'm doing a network call userRepo.Login() and if an exception happens I show an error message and stop the progress animation that I started at the start of the function.

If I leave everything on the CommonPool (or don't add any pool) it crashes saying the animation must be done on a looper thread if an exception happens. In other circumstances I've received errors saying this must be done on the UI thread as well, same problem, different thread requirements.

I can't launch the whole coroutine on the UI thread though, because the login call will block since it's on the UI thread and messes up my animation (which makes sense).

The only way I can see to resolve this, is the launch a new coroutine on the UI thread from within the existing coroutine, which works, but seems weird.

Is this the proper way to do things, or am I missing something?

override fun loginButtonPressed(email: String, password: String) {

    view.showSignInProgressAnimation()

    launch(CommonPool) {
        try { 
            val user = userRepo.login(email, password)

            if (user != null) {
                view.launchMainActivity()
            }

        } catch (exception: AuthException) {
            launch(UI) {
                view.showErrorMessage(exception.message, exception.code)
                view.stopSignInProgressAnimation()
            }
        }
    }
}
like image 309
Ben987654 Avatar asked May 03 '18 23:05

Ben987654


People also ask

Do coroutines use multiple threads?

coroutines library provides support for using multiple threads. It is a separate branch for the reasons listed in the future concurrency model blog post. However, you can still use the multithreaded version of kotlinx.

Do coroutines in Kotlin allocate new threads?

Kotlin coroutines introduce a new style of concurrency that can be used on Android to simplify async code. The official documentation says that coroutines are lightweight threads. By lightweight, it means that creating coroutines doesn't allocate new threads.

What are coroutines does it run on different thread?

The block of code passed to a coroutine builder ends up executing on one or multiple threads. And as such, coroutines run on the Android runtime threading model with all its constraints. With coroutines, it's still possible to write vulnerable wrong multi-threaded code.

What is the best way for performing tasks on a background thread in Kotlin?

We can do that by using callbacks. In this example, the callback is executed in the calling thread, which is a background thread. This means that you cannot modify or communicate directly with the UI layer until you switch back to the main thread.


1 Answers

You should start from the opposite end: launch a UI-based coroutine, from which you hand off heavy operations to an external pool. The tool of choice is withContext():

override fun loginButtonPressed(email: String, password: String) {
    view.showSignInProgressAnimation()
    // assuming `this` is a CoroutineScope with dispatcher = Main...
    this.launch {
        try {
            val user = withContext(IO) { 
                userRepo.login(email, password) 
            }
            if (user != null) {
                view.launchMainActivity()
            }
        } catch (exception: AuthException) {
            view.showErrorMessage(exception.message, exception.code)
            view.stopSignInProgressAnimation()
        }
    }
}

This way you keep your natural Android programming model, which assumes the GUI thread.

like image 191
Marko Topolnik Avatar answered Sep 24 '22 22:09

Marko Topolnik