When and how to call suspend function from Android Activity?

12,866

Solution 1

By default runBlocking runs the suspending code block in the thread runBlocking was called on.

So if you called runBlocking from the Activity callback your suspending block will be executed on the main thread, from which you cannot access network (query a server).

You need to switch a dispatcher in your coroutine block for that call. The simplest fix for your code would be to move the execution to the Dispatchers.IO.

runBlocking {
    withContext(Dispatchers.IO) {
        retrieveInfo()
    }
}

That being said, I suggest two things (not related directly to your question):

  1. Read Coroutines on Android (this part and the following ones)

2. Don't use runBlocking for your case, but define a correct job and use job.launch{}

Solution 2

If you want to write in activity:

class MyActivity : AppCompatActivity() {
private val scope = CoroutineScope(newSingleThreadContext("name"))

fun doSomething() {
    scope.launch { ... }
  }
}
Share:
12,866

Related videos on Youtube

user3087615
Author by

user3087615

Updated on September 16, 2022

Comments

  • user3087615
    user3087615 over 1 year

    I have a suspend function that calls POST request to the server. I want to configure some text in the activity to show the information I received from the server.

    suspend fun retrieveInfo():String 
    

    I tried calling inside onCreate, onResume but crashes runtime.

    runBlocking { 
        retrieveInfo()
    }
    
        java.lang.RuntimeException: Unable to start activity ComponentInfo{com.google.augmentedimage/com.google.AugmentedImageActivity}: android.os.NetworkOnMainThreadException
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3086)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3229)
            at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
    
    

    Where am I suppose to put these suspend calls (in which part of lifecycle of activity)? Should I be using something other than runBlocking?

    • Taseer
      Taseer almost 5 years
      Launch a coroutine from your main thread. CoroutineScope(Dispatchers.Main).launch { withContext(Dispatchers.IO) { myFun() }
    • John O'Reilly
      John O'Reilly almost 5 years
      Generally the favoured approach for something like this is to use a ViewModel and then use it's built in viewModelScope (some more details in medium.com/androiddevelopers/…)
  • user3087615
    user3087615 almost 5 years
    Your quick solution of runBlocking with IO context allows the code to run. However, unless the API call is fast enough, it still gives Unable to start activity ComponentInfo java.net.SocketTimeoutException
  • Bartek Lipinski
    Bartek Lipinski almost 5 years
    @user3087615 that's a whole another issue unrelated to the coroutine itself. You need to have a proper error handling for your api call.
  • Bartek Lipinski
    Bartek Lipinski almost 5 years
    @user3087615 also you really should follow the second point of my answer because calling runBlocking in Activity lifecycle methods is a really bad idea.
  • user3087615
    user3087615 almost 5 years
    is there any good documentation on how to create a job and launch? I am new to kotlin and struggling to find good documentation. If you could show a short example or point to documentation, it would be awesome.
  • Bartek Lipinski
    Bartek Lipinski almost 5 years
    @user3087615 my post consists of a great post on the subject
  • Slava
    Slava almost 5 years
    Not really, please, provide a better one :(
  • Akito
    Akito over 2 years
    newSingleThreadContext is obsolete.