PhillyTheThrilly
PhillyTheThrilly

Reputation: 1842

What is the proper way to launch a coroutine on Android?

I'm trying to figure out how to launch a coroutine. I want it to call two suspend functions in sequence.

The first docs I read said to do this:

class Something {
  fun initialize() {
    launch {
      suspendFun1()
      suspendFun2()
    }
}

But the launch method was not found by Android Studio. Then I learned that the official coroutine docs suggest using GlobalScope.launch:

class Something {
  fun initialize() {
    GlobalScope.launch {
      suspendFun1()
      suspendFun2()
    }
}

But then I read in this post that you should not use GlobalScope.launch.

So I found another blog post explaining that I need a CoroutineScope to call launch. But it doesn't explain how to build one.

Then I found this blog post explaining how to build one for Activities and ViewModels, by implementing CoroutineScope on the class:

class Something : CoroutineScope {
  private lateinit var job: Job
  override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Main

  fun initialize() {
    job = Job()
    launch {
      suspendFun1()
      suspendFun2()
    }
}

Then I read this blog post saying I should not implement CoroutineScope

class Something {
  protected val scope = CoroutineScope(
    Job() + Dispatchers.Main
  )

  fun initialize() {
    scope.launch {
      suspendFun1()
      suspendFun2()
    }
}

But I'm not sure I understand the meaning of Job() + Dispatchers.Main, as this seems to work as well:

class Something {
  protected val scope = CoroutineScope(Dispatchers.Main)

  fun initialize() {
    scope.launch {
      suspendFun1()
      suspendFun2()
    }
}

Can someone explain to me simply the best way to do this? Is it as above? I'm sure this has been asked already, so I apologize if this is a duplicate question. But as you can see, there are not clear answers on this. I would like to hear your input.

Upvotes: 44

Views: 28551

Answers (4)

Bernd Kampl
Bernd Kampl

Reputation: 6299

For someone just looking for a simple way to launch a Coroutine with an Android Activity without all the bells and whistles, here's a quick and simple way (but you should still look into learning how to use Coroutines correctly, i.e. with ViewModels & CoroutineScope)

  1. Add androidx.lifecycle to your app-level build.gradle:

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1" (check Link above for current version)

  1. In the Activity, use the Activity-provided LifecycleScope to launch the Coroutine (mostly pseudo-code):
import androidx.lifecycle.lifecycleScope

class YourActivity {

  override fun onCreate() {
    lifecycleScope.launch {
      // do your Coroutine Stuff here, i.e. call a suspend fun:
      coroutineFunction()
    }
  }

  suspend fun coroutineFunction() {
    // Use a different CoroutineScope, etc
    CoroutineScope(Dispatchers.IO).launch {
      // do some long running operation or something
    }
  }

}

Upvotes: 24

Chee-Yi
Chee-Yi

Reputation: 950

I was in a very similar dilemma, and the OP brilliantly captured a newbie's confusion going about the various resources. In my case, I have no choice other than to create my own CoroutineScope because I needed a long-running scope that lives as long as the app process does (it handles interacting with Retrofit using suspend functions), and it lives inside an Android library, having no access to ViewModel and other LifecycleOwner items.

/**
 * A [CoroutineScope] for HTTP cloud implementations to launch coroutines in. A coroutine
 * dispatcher is not specified because Retrofit will switch to its own custom dispatcher
 * when performing suspending network calls.
 */
internal val networkingScope = CoroutineScope(SupervisorJob())

Upvotes: 0

Yousef
Yousef

Reputation: 385

I have Coroutines.kt

object Coroutines {

    fun main(work: suspend (() -> Unit)) {
        CoroutineScope(Dispatchers.Main).launch {
            work()
        }
    }

}

and I use it in my viewmodels by calling Couroutines.main

Upvotes: 13

Pawel
Pawel

Reputation: 17288

Last two solutions are fine. CoroutineScope(context: CoroutineContext) creates an empty Job if you pass a context without one.

Specifying job + dispatcher is just more explicit way to build it as in some cases you might want to use a SupervisorJob instead to prevent entire scope from being canceled when one of child jobs fail.

As for building scopes in Activities and ViewModels instead of declaring your own you can use ones built-in into KTX library by importing KTX fragment module:

// add to your apps dependencies
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc02'

Now inside your Activitys and Fragments you can use lifecycleScope and inside ViewModel a viewModelScope which are scopes backed by SupervisorJob + Dispatchers.Main.immediate and are automatically canceled when their respective lifecycle is destroyed.

Upvotes: 15

Related Questions