When and how to call suspend function from Android Activity?
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):
- 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 { ... }
}
}
Related videos on Youtube
user3087615
Updated on September 16, 2022Comments
-
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 almost 5 yearsLaunch a coroutine from your main thread.
CoroutineScope(Dispatchers.Main).launch { withContext(Dispatchers.IO) { myFun() }
-
John O'Reilly almost 5 yearsGenerally the favoured approach for something like this is to use a
ViewModel
and then use it's built inviewModelScope
(some more details in medium.com/androiddevelopers/…)
-
-
user3087615 almost 5 yearsYour quick solution of
runBlocking
withIO
context allows the code to run. However, unless the API call is fast enough, it still givesUnable to start activity ComponentInfo
java.net.SocketTimeoutException
-
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 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 almost 5 yearsis 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 almost 5 years@user3087615 my post consists of a great post on the subject
-
Slava almost 5 yearsNot really, please, provide a better one :(
-
Akito over 2 years
newSingleThreadContext
is obsolete.