Coroutines and suspend related to main thread and dispatcher - android

I have read one article which says the following :
* Room will provide main-safety automatically if you use suspend functions, RxJava, or LiveData.
** Networking libraries such as Retrofit and Volley manage their own threads and do not require explicit main-safety in your code when used with Kotlin coroutines.
So I have two questions :
If i will have one suspend function inside viewmodel and it's having long running task inside it and it does not use any dispatcher. So if I will call this function from activity/fragment, then will it work as simple function as we have not defined any dispatcher and will block the ui ?
As stated above in the statements, for room/retrofit, should we use dispatcher explicitly(like IO in these cases) as they are taking care of background thread by themselves.
Googled it, did not get exact answer, so posting to get clarity.

Yes, the suspended function will run normally & will not block the UI unless you use a blocking coroutine like runBlocking or withContext that returns a value to the UI.
A simple launch i.e. viewModelScope.launch would not block a thread.
As per the docs they use a custom dispatcher to handle threading.
From the code-lab docs:
Both Room and Retrofit make suspending functions main-safe.
It's safe to call these suspend funs from Dispatchers.Main, even though they
fetch from the network and write to the database.
Both Room and Retrofit use a custom dispatcher and do not use
Dispatchers.IO.
Room will run coroutines using the default query and
transaction Executor that's configured.
Retrofit will create a new Call object under the hood, and call
enqueue on it to send the request asynchronously.

As my understand,
When you call suspend function, you need to provide a coroutine scope. So, if you provide Dispatcher.Main or MainScope, it will block UI.
Room/Retrofit has implicit coroutine scope. That means you don't need provide it for them. But when you call to Room/Retrofit, you will need to provide coroutine scope like [1]

Related

How important is it to specify dispatchers/context in Kotlin coroutines? What happens if you don't specify them?

If a coroutine was launched and no dispatcher was specified (eg. GlobalScope.launch {}), what dispatcher is used?
If that coroutine was launched in the main thread, would it use Dispatchers.main?
Also, what happens if you do not specify dispatchers or context in your coroutines? Say you did database operations, but didn't specify Dispatchers.IO anywhere. Would that cause any major issues?
If a coroutine was launched and no dispatcher was specified (eg. GlobalScope.launch {}), what dispatcher is used?
If no dispatcher is specified in the coroutine builder, you get the dispatcher from whichever CoroutineScope you're starting your coroutine in. If there is no dispatcher in that scope's context, the coroutine will use Dispatchers.Default (see the doc of launch for instance).
Note that the scope is the receiver of the coroutine builder call:
if you see GlobalScope.launch { ... } then GlobalScope is the scope
if you see scope.launch { ... }, look at that scope
if you see launch { .. } in the wild, some instance of CoroutineScope must be available as this in that piece of code, so that's the parent scope (see below for an example on where it could be coming from)
Here is some info about the dispatchers used in the most common coroutine scopes:
If the scope is GlobalScope, then it doesn't have any dispatcher, so as mentioned before the coroutines will use Dispatchers.Default.
If the scope is lifecycleScope or viewModelScope provided by Android, then coroutines will use Dispatchers.Main.immediate by default.
If the scope is created with the CoroutineScope() factory function without particular dispatcher, Dispatchers.Default will be used (see the "Custom usage" section in the documentation).
If the scope is created using MainScope() and without particular dispatcher, it will use Dispatchers.Main as per the same documentation.
If the scope is provided by runBlocking, then it will use a special dispatcher that works like an event loop and executes your coroutines in the thread that called runBlocking (which is nice because that thread would be blocked anyway).
If the scope is provided by another (outer) coroutine builder like launch or async (which means your coroutine is a child of that coroutine), then the dispatcher will be taken from the scope that was used to launch the parent coroutine, unless they override it. So you can go all the way up until you reach one of the options mentioned above:
parentScope.launch {
// here, this = child scope that gets the dispatcher from parentScope
launch {
// inherits from parent launch
}
launch(Dispatchers.IO) {
// here, this = child scope with overridden dispatcher
launch {
// inherits Dispatchers.IO from parent launch's scope
}
}
}
If that coroutine was launched in the main thread, would it use Dispatchers.main?
You probably can piece this together with the previous paragraph, but just to reiterate: it depends on the scope you're launching your coroutine in.
If you launch your coroutines from lifecycleScope/viewModelScope, then yes it will run on the main thread (in Dispatchers.Main.immediate).
If you call runBlocking from the main thread, then it would run coroutines on the main thread as well (but not in Dispatchers.Main), but you should definitely never do that in Android.
If you use something like MainScope() it would indeed run in Dispatcher.Main.
But in other cases, there is a high chance it runs on Dispatchers.Default - check the scope!
Also, what happens if you do not specify dispatchers or context in your coroutines? Say you did database operations, but didn't specify Dispatchers.IO anywhere. Would that cause any major issues?
As you can see from the beginning of this answer, the dispatcher used depends on how you coroutine is launched. If we assume you don't specify any dispatcher anywhere, there is a good chance your coroutines are running on Dispatchers.Main (in Android) or Dispatchers.Default.
Dispatchers.Main would be really bad for IO stuff, because it would freeze your UI while the IO is happening. I believe Android probably crashes in case you run the wrong things in this dispatcher, but I haven't done Android dev in a while, so I can't say for sure.
Dispatchers.Default is a shared thread pool that's sized based on the number of cores of the machine executing the code, so it's suitable for CPU-bound tasks. If you launch a bunch of coroutines that perform blocking IO in this dispatcher, you could block all your threads and prevent other coroutines from running, which is really not ideal - it could cause lags or slowness, especially if you rely a lot on coroutines.
Dispatchers.IO is not magic, but it will spawn more threads as necessary if too many threads are blocked, so that other coroutines can run. You will still incur the additional memory of extra threads, but other coroutines will be free to run while some threads are blocked on IO.
You can read more about how to use dispatchers in the documentation.

Why would anyone use execute() function in Retrofit

I know that execute() is a synchronous function which means Until you are able to use it should execute it in other threads.
But I want to understand why would I use execute function even though enqueue function exists that does this work (execute on other thread) itself.
what are cases that should use execute function in it?
Sometimes, you are already on a background thread, supplied by something else:
JobIntentService
WorkManager
Kotlin coroutines
RxJava
Etc.
In those cases, you may not need OkHttp or Retrofit to use yet another background thread and can use execute() for simpler code.

best way for running code in async with kotlin

hi i want use jsoup to load a large table from html, what is the best way for doing this in async way?
AsyncTask? coroutines? doasync library? which one? i need show progressbar while fetching data so please tell me what is the best way?
UPDATE:
i want run this code in async
doc: Document = Jsoup.connect(url).timeout(0).maxBodySize(0).ignoreHttpErrors(true).sslSocketFactory(setTrustAllCerts()).get()
// some code for parsing...
In Kotlin, the general approach is coroutines, but normal threading is also a completely fine option, depending on what you're doing.
For example, if your operation is a thread-blocking operation, it actually can't run safely in a coroutine unless it's dispatched in a separate thread. For coroutines, you need to know the difference between suspending and blocking (huge difference).
So if reading the HTML table is a blocking operation, and you don't need to integrate with other coroutines, then a normal thread works just fine. There are many Java examples that are transferable to Kotlin.
With coroutines, you can do something like:
suspend fun getDoc() = withContext(Dispatchers.IO) {
Jsoup.connect(url).timeout(0).maxBodySize(0).ignoreHttpErrors(true).sslSocketFactory(setTrustAllCerts()).get()
}
Then, in your main code:
fun main() = runBlocking {
val deferredDoc = async { getDoc() }
// Do whatever.... it's not being blocked...
val doc = deferredDoc.await() // SUSPENDING CALL - but not blocking
}
Obviously, your program's structure will look different than this example, because it depends entirely on what code you want to execute asynchronously with "getDoc()".
For example, you can even have another coroutine that executes while "deferredDoc.await()" is suspending, without even creating another thread. That's the benefit of coroutines.
In the structure above, we have 3 guaranteed threads:
Main thread, which is always blocked
Main Coroutine thread. This is what the coroutines generally run on. Kotlin coroutines will run your coroutines asynchronously inside this thread using suspension.
IO thread. This is what your blocking code will run on.
I'll advice you try out Kotlin Coroutines. This would enable you dispatch expensive or long-running operations i.e. querying databases, making network requests/calls off to other threads thereby not blocking the Main Thread. Coroutines help you avoid the hassle of callbacks. Also, Google deprecated the AsyncTask API (in Android 11) and recommends using Java’s Concurrency framework or Kotlin Coroutines as the way to go for multi-threading purposes.

What happens if a non-suspend function is called from a coroutine?

One of the key concepts of coroutines in Kotlin is that a suspend function must be called from a coroutine or another suspend function.
However, a suspend function can call any kind of function, suspend or normal.
What is the consequence of that? (This is not a real scenario i have, just want to know for theoretical reasons)
I imagine that in that scenario the only point of creating a coroutine would be to change the context (thread) before calling it, so it doesn´t block the main thread.
However, would all the other advantages of coroutines be lost? (cooperative cancellation, structured concurrency...)
If a suspending function calls another suspending function, then the coroutine is suspended, until the result is returned.
Calling a regular function from a suspending function will block the thread. Which thread? Well, that depends on the Dispatcher you're using. IO is able to spawn hundred of threads. But the Default dispatcher has same amount of threads as your CPUs count. Meaning that while this won't block other coroutines, it will reduce the amount of available resources.
Meaning: don't invoke non-suspending function that may block for a long period of time on this dispatcher, same as you don't block your UI thread.
And yes, suspending function may produce the same results, if you're do something like a busy loop without yield() or any other suspend invocation in it.
Suspend functions can be suspended in between and resumed later. Calling a normal function from a suspend function you lose the ability to pause the execution.
when a suspend function is compiled, kotlin compiler adds a continuation object as a parameter in the arguments. This continuation object is required for suspending a coroutine. When a suspend function calls another suspend function it passes the continuation object same is true with co-routines.
if a non-suspend function is called from suspend function you just wont be able to use co-routines from that function... that's it
However, would all the other advantages of coroutines be lost? (cooperative cancellation, structured concurrency...)
Even a non-suspendable function can participate in cooperative cancellation by explicitly checking the CoroutineContext.isActive flag. This is more of a theoretical fact, though.
If a function performs a blocking IO operation, it will not respond to cancellation requests. The operation will have to complete on its own.
As for structured concurrency, almost the opposite of your concern is true: it is a Kotlin best practice to launch child coroutines from a non-suspendable function that is instead an extension on CoroutineScope and calls the coroutine builders with it as the receiver.

How does a Coroutine Continuation internally work?

I have been working with coroutines for few weeks and sometimes its tough to understand the real working difference between thread concurrency and coroutine concurrency.
How suspend functions works internally ? How is continuation block helping in resuming the computation after suspension.
How is sequential computation of the line of code inside coroutine not blocking the thread ? and how is it better than thread concurrency ?
How suspend functions works internally?
Briefly, on the Java platform a suspend fun compiles into bytecode that is dramatically different from a plain function. It receives a hidden extra parameter (the continuation), creates its own continuation object, and the entire body of the function is implemented (approximately) as a big switch statement that allows the function to jump into the middle of the body when resuming.
When a suspend fun suspends, the underlying Java method actually returns. The return value is a special COROUTINE_SUSPENDED singleton object which the framework knows how to interpret. It is the responsibility of the suspend fun itself to save the continuation object where it will be accessible when the result of the function is ready.
The official documentation has a good in-depth description of these details.
How is continuation block helping in resuming the computation after suspension.
This is related to what I said above, that the suspend fun itself is responsible for ensuring it gets resumed later on. It must do that inside the block provided by the function suspendCoroutineOrReturn. User code doesn't call it directly, but the more high-level analogs suspendCoroutine and suspendCancellableCoroutine. These take over the concern of resuming the coroutine on the appropriate thread and the developer is responsible only for ensuring that continuation.resume() is called with the result when it becomes available. This typically happens in a callback you pass to an async call.
You can study this answer that tries to explain the suspend-resume mechanism in a self-contained example.
How is sequential computation of the line of code inside coroutine not blocking the thread?
Because it actually compiles into returning from the function and later on resuming by jumping into the middle of the function body.
and how is it better than thread concurrency?
Native threads are heavyweight resources that take time to create and destroy. Coroutines are much lighter-weight and consequently you can start many more of them, and more quickly.
The internal workings are explained in the original design document https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md which has a section on "Implementation Details".

Categories

Resources