Meaning of this code "suspend fun get(url: String) = withContext(Dispatchers.IO){/*...*/}" - android

I am new to android development.When I was reading medium post https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb I came across this code:
suspend fun get(url: String) = withContext(Dispatchers.IO){/*...*/}
which I could not understand. I have tried Search but I could not find code with similar syntax.Can somebody please explain it?

It's releated to asynchronous or non-blocking programming using Coroutines. It's a suspending function which can suspend the execution of a coroutine.
The withContext lets your function return a value ( you can also use launch which will return a job ).
From docs:
Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns the result. Read more here.
The Dispatchers.IO is the default instance of coroutine dispatcher for background coroutine. Read more here.

Related

suspend method inside runInTransaction block

I have a compilation error using the code below:
Suspension functions can be called only within coroutine body
Can someone explain to me why? What do I need to do to make it work (without using the #Transaction annotation)?
override suspend fun replaceAccounts(newAccounts: List<Account>) {
database.runInTransaction {
database.accountDao().deleteAllAccounts() // I have the error on this line
database.accountDao().insertAccounts(newAccounts) // Here too
}
}
#Dao
abstract class AccountDao : BaseDao<AccountEntity> {
#Query("DELETE FROM Account")
abstract suspend fun deleteAllAccounts()
}
Thanks in advance for your help
For suspend functions you should use withTransaction instead of runInTransaction
IO-bound and other long-running operations (such as database or API calls) are restricted from running in the main thread directly (which could otherwise cause your program to become unresponsive). Coroutines are like light-weight threads which run, asynchronously, within a thread.
I suggest reading through the Coroutines guide at https://kotlinlang.org/docs/reference/coroutines/coroutine-context-and-dispatchers.html
To answer your question, you need to setup a coroutine scope, and dispatcher thread for your coroutine to run on. The simplest is something like:
GlobalScope.launch(Dispatchers.IO) {
replaceAccounts(newAccounts)
}
which would run your coroutine in the GlobalScope (the coroutine's "lifecycle" is bound to the lifecycle of the entire application), on the IO thread (a thread outside of the main thread which handles IO tasks).
EDIT
I do like #IR42's answer. To build upon that, the usage of withTransaction in this case allows Room to handle the thread in which the database operations are performed on, and helps to limit concurrency to the database.
GlobalScope.launch(Dispatchers.Main) {
replaceAccounts(newAccounts)
}
override suspend fun replaceAccounts(newAccounts: List<Account>) {
database.withTransaction {
database.accountDao().deleteAllAccounts() // I have the error on this line
database.accountDao().insertAccounts(newAccounts) // Here too
}
}
See more information on this article by one of Room's own: https://medium.com/androiddevelopers/threading-models-in-coroutines-and-android-sqlite-api-6cab11f7eb90

Perform network task in context of Dispatcher.Main

according to help, long network tasks should be performed in the context of Dispatcher.IO.
But why couldn't use suspend function like get in Dispatcher.Main context? Thread itself isn't blocked, so do we expect any problem from code like:
GlobalScope.launch(Dispatchers.Main) {
val client = HttpClient(Android)
var data: String = client.get('http://example.com')
}
assuming get is suspend function taking much time.
Thanks.
You are right here. You can make that network request in Dispatchers.Main.
It seems to be a common misconception, that just because IO is being performed by a suspend function, you must call it in Dispatchers.IO, which is unnecessary (and can be expensive).
suspending functions by convention don't block the calling thread and internally blocks in Dispatchers.IO if need be.

Does the suspend keyword in Kotlin do anything without coroutines?

My understanding is that Kotlin's coroutines are libraries, which leaves the only language-level feature of concurrency in Kotlin as the suspend keyword.
I'm still wrapping my head around coroutines in Kotlin, but I'm wondering if that may be overkill for my problem, which is updating a text view as soon as an HttpsURLConnection returns data. exception handling makes callbacks ugly enough that I want to avoid those if possible
does the suspend keyword simply mean that the runtime may suspend a function that takes a while to complete? or is suspension only enabled inside a coroutine? as a hypothetical, can I write
suspend fun getStringFromNetwork(): String {
val request = URL("https:stackoverflow.com").openConnection()
val result = readStream(request.inputStream)
request.disconnect()
return result
}
//and then elsewhere
foo()
val s = getStringFromNetwork()
bar(s)
baz()
and know that if getStringFromNetwork downloads 1 GB of data that baz() will be called in the meantime, while bar(s) waits for s to be populated by getStringFromNetwork?
The "and then elsewhere" part calls getStringFromNetwork(), so it won't compile outside a suspend function (including suspend lambdas), and they can only be executed inside coroutines.
that baz() will be called in the meantime, while bar(s) waits for s to be populated by getStringFromNetwork?
No, if you write it this way, baz() will only start executing after bar(s) returns. But of course bar(s) can start a new coroutine which will do the actual work.

Kotlin coroutines `runBlocking`

I am learning Kotlin coroutines. I've read that runBlocking is the way to bridge synchronous and asynchronous code. But what is the performance gain if the runBlocking stops the UI thread?
For example, I need to query a database in Android:
val result: Int
get() = runBlocking { queryDatabase().await() }
private fun queryDatabase(): Deferred<Int> {
return async {
var cursor: Cursor? = null
var queryResult: Int = 0
val sqlQuery = "SELECT COUNT(ID) FROM TABLE..."
try {
cursor = getHelper().readableDatabase.query(sqlQuery)
cursor?.moveToFirst()
queryResult = cursor?.getInt(0) ?: 0
} catch (e: Exception) {
Log.e(TAG, e.localizedMessage)
} finally {
cursor?.close()
}
return#async queryResult
}
}
Querying the database would stop the main thread, so it seems that it would take the same amount of time as synchronous code? Please correct me if I am missing something.
runBlocking is the way to bridge synchronous and asynchronous code
I keep bumping into this phrase and it's very misleading.
runBlocking is almost never a tool you use in production. It undoes the asynchronous, non-blocking nature of coroutines. You can use it if you happen to already have some coroutine-based code that you want to use in a context where coroutines provide no value: in blocking calls. One typical use is JUnit testing, where the test method must just sit and wait for the coroutine to complete.
You can also use it while playing around with coroutines, inside your main method.
The misuse of runBlocking has become so widespread that the Kotlin team actually tried to add a fail-fast check which would immediately crash your code if you call it on the UI thread. By the time they did this, it was already breaking so much code that they had to remove it.
Actually you use runBlocking to call suspending functions in "blocking" code that otherwise wouldn't be callable there or in other words: you use it to call suspend functions outside of the coroutine context (in your example the block passed to async is the suspend function). Also (more obvious, as the name itself implies already), the call then is a blocking call. So in your example it is executed as if there wasn't something like async in place. It waits (blocks interruptibly) until everything within the runBlocking-block is finished.
For example assume a function in your library as follows:
suspend fun demo() : Any = TODO()
This method would not be callable from, e.g. main. For such a case you use runBlocking then, e.g.:
fun main(args: Array<String>) {
// demo() // this alone wouldn't compile... Error:() Kotlin: Suspend function 'demo' should be called only from a coroutine or another suspend function
// whereas the following works as intended:
runBlocking {
demo()
} // it also waits until demo()-call is finished which wouldn't happen if you use launch
}
Regarding performance gain: actually your application may rather be more responsive instead of being more performant (sometimes also more performant, e.g. if you have multiple parallel actions instead of several sequential ones). In your example however you already block when you assign the variable, so I would say that your app doesn't get more responsive yet. You may rather want to call your query asynchronously and then update the UI as soon as the response is available. So you basically just omit runBlocking and rather use something like launch. You may also be interested in Guide to UI programming with coroutines.

Kotlin Android Coroutines - suspend function doesn't seem to run in the background

I feel like i'm missing some crucial part to my understanding to how this code below is working:
private fun retrieveAndStore() {
launch(UI) {
val service = retrofit.create(AWSService::class.java)
val response = service.retrieveData().await()
store(data = response)
}
}
private suspend fun store(data: JsonData) {
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "app-db").build()
db.appDao().insert(storyData)
}
This is the error i get when run:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
I don't understand why the network code via retrofit works but the store function fails. I'm hoping someone can tell me what's going on?
Interestingly if i wrap db call with async {}.await it works, does that mean coroutines can only call other coroutines?
Coroutines aren't about running in either foreground or background. They are about the ability to get suspended, just like a native thread gets suspended by the OS, but on the level where you are in control of that behavior.
When you say launch(UI) { some code }, you tell Kotlin to submit "some code" as a task to the GUI event loop. It will run on the GUI thread until explicitly suspended; the only difference is that it won't run right away so the next line of code below the launch(UI) block will run before it.
The magic part comes around when your "some code" encounters a suspendCoroutine call: this is where its execution stops and you get a continuation object inside the block you pass to suspendCoroutine. You can do with that object whatever you want, typically store it somewhere and then resume it later on.
Often you don't see the suspendCoroutine call because it's inside the implementation of some suspend fun you're calling, but you can freely implement your own.
One such library function is withContext and it's the one you need to solve your problem. It creates another coroutine with the block you pass it, submits that coroutine to some other context you specify (a useful example is CommonPool) and then suspends the current coroutine until that other one completes. This is exactly what you need to turn a blocking call into a suspendable function.
In your case, it would look like this:
private suspend fun store(data: JsonData) = withContext(CommonPool) {
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "app-db").build()
db.appDao().insert(storyData)
}
I'll also add that you're better off creating your own threadpool instead of relying on the system-wide CommonPool. Refer to this thread for details.

Categories

Resources