I have a method
fun refrehList() {
viewModelScope.launch {
myData.value = withContext(Dispatchers.Default) {
summaryRepository.getSummaries(true)
}
allData.value = withContext(Dispatchers.Default) {
SummaryRepository.getSummaries(false)
}
}
}
Is this correct way of using coroutine. Is the DB operation happening in the background scope
If you're using Room, its documentation states the following:
You can add the suspend Kotlin keyword to your DAO methods to make
them asynchronous using Kotlin coroutines functionality. This ensures
that they cannot be executed on the main thread.
So you will be safe calling your repository inside the viewModelScope without changing context.
You can find that Room's documentation section here.
Yes this code will run on separate thread but one after another. Also you should be using Dispatchers.IO for database calls instead of Dispatchers.Default See Io vs Default.
viewModelScope.launch {
myData.value = withContext(Dispatchers.IO) {
Log.e("thread1", Thread.currentThread().name)
summaryRepository.getSummaries(true)
}
Log.e("thread2", Thread.currentThread().name)
allData.value = withContext(Dispatchers.IO) {
Log.e("thread3", Thread.currentThread().name)
SummaryRepository.getSummaries(false)
}
}
This will print something like :-
E/thread: DefaultDispatcher-worker-1
E/thread2: main
E/thread3: DefaultDispatcher-worker-1
If you want to run those co-routine in parallel you can use async-await .
Related
I've to update an item in a fragment based on the data returned from the network call. I don't want to block the main thread and end up with ANR so I adopted the approach of "callbacks" however I wonder if there's a way to wait for the result from the network call without relying on the callback mechanism using coroutines
Current implementation
MyFragment.kt
fun updateButtonText() {
handlerClass.getData {
//received data from the server update the button text
}
}
HandlerClass.kt
fun getData(callback: (String) -> Unit) {
scope.launch(Dispatchers.IO) {
val data = mySuspendedNetworkcallMethod()
callback.invoke(data)
}
}
Desired Implementation:
MyFragment.kt
fun updateButtonText() {
val data = handlerClass.getData()
button.text = data
}
HandlerClass.kt
suspend fun getData() {
return mySuspendedNetworkcallMethod()
}
For the desired demo implementation, I understand, I'd have to use runBlocking{} to call a suspended method however runBlocking{} will block the calling thread - which in this case would be the Main Thread until getData() returns the data.
I don't want to block the main thread but still be able to cal & wait for the suspended method to fetch the data and then update the button.
Coroutines are designed to get rid of callbacks. You can use lifecycleScope in the Fragment class to launch a lifecycle-aware coroutine, it will look like the following:
MyFragment.kt:
fun updateButtonText() = lifecycleScope.launch {
button.text = handlerClass.getData()
}
HandlerClass.kt:
suspend fun getData() {
return mySuspendedNetworkcallMethod()
}
If you use MVVM approach you should consider to use ViewModel and it's viewModelScope extension to launch coroutines.
For LifecycleScope, use androidx.lifecycle:lifecycle-runtime-ktx:2.4.0 or higher.
For ViewModelScope, use androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0 or higher.
Well recommended way is to use viewmodel and viewmodelscope for suspend functions.
However in your situation, just use lifecyclescope
fun updateButtonText() {
lifecycleScope.launch{
val data = handlerClass.getData()
button.text = data
}
}
https://developer.android.com/topic/libraries/architecture/coroutines
I am using OkHttp library to download some data from the internet in my androidx.lifecycle.ViewModel
I then want to update my LiveData. It seems that doing it from background thread throws exception like so:
2022-01-17 15:47:59.589 7354-7396/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
Process: com.example.myapplication, PID: 7354
java.lang.IllegalStateException: Cannot invoke setValue on a background thread
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:487)
at androidx.lifecycle.LiveData.setValue(LiveData.java:306)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at com.example.myapplication.MainActivityViewModel$getOneMoreCat$1.invoke(MainActivityViewModel.kt:86)
at com.example.myapplication.MainActivityViewModel$getOneMoreCat$1.invoke(MainActivityViewModel.kt:39)
at com.example.myapplication.singleton.CommunicationManager$sendRequest$1.onResponse(CommunicationManager.kt:24)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
Now I found two different ways to dispatch to main thread from ViewModel (which has no reference to Context as per AAC guidelines), see here:
GlobalScope.launch {
withContext(Dispatchers.Main) {
// do whatever, e.g. update LiveData
}
}
or
Handler(Looper.getMainLooper()).post(Runnable {
// do whatever, e.g. update LiveData
})
Which is the correct way? That is, least impactful at runtime.
Update I did find that I can also do myLiveData.post() and it works from background thread.
Still, I'd like to know what is the correct way to dispatch work to main thread in modern Android under kotlin
The right way to dispatch work from Background Thread to Main Thread using LivaData is to use LivaData.postValue() method. It posts a task to a main thread to set the given value.
Another approach is to use viewModelScope extension property in ViewModel class, by default it uses Dispatchers.Main context to execute a coroutine, it means you can update UI in such coroutine. For example, in your ViewModel class:
viewModelScope.launch {
val result = makeNetworkCall()
// use result to update UI
liveData.value = result
}
// withContext - switches context to background thread
suspend fun makeNetworkCall(): String = withContext(Dispatchers.IO) {
delay(1000) // simulate network call
"SomeResult"
}
Dependency to use viewModelScope:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
GlobalScope is highly discouraged to use, it can only be used in specific cases, here is a description why not use it.
Inside viewmodel,
private val _downloading = MutableLiveData<Result<Boolean>>()
val downloading: LiveData<Result<Boolean>>
get() = _downloading
fun downloadFile() {
viewModelScope.launch {
try {
_downloading.value = Result.Loading
val result = withContext(Dispatchers.IO) {
// download something
}
_downloading.value = Result.Success(true)
} catch (ex: Exception) {
_downloading.value = Result.Failure(ex)
}
}
}
In activity/fragment,
viewModel.downloading.observe(this, {
when (it) {
is Result.Failure -> TODO()
Result.Loading -> TODO()
is Result.Success -> TODO()
}
})
Result is a sealed class to capture state, which in turn will help us update the UI accordingly. Also viewmodelscope is used instead of GlobalScope since we don't want the download to go on when the viewmodel is destroyed.
there are many ways to do that you can simply post value to live data, using dispatcher's and handler which is running on main thread as you provide looper of main thread.
Another way is you can use high order functions to update the viewmodels which is easy to use and give it a try.
While I was learning coroutines and how to properly use them in an android app I found something I was surprised about.
When launching a coroutine using viewModelScope.launch { } and setting a breakpoint inside the launch lambda I noticed my app wasn't responsive anymore because it was still on the main thread.
This confuses me because the docs of viewModelScope.launch { } clearly state:
Launches a new coroutine without blocking the current thread
Isn't the current thread the main thread ? What is the whole purpose of launch if it doesn't run on a different thread by default ?
I was able to run it on anther thread using viewModelScope.launch(Dispatchers.IO){ } which works as I was expecting, namely on another thread.
What I am trying to accomplish from the launch method is to call a repository and do some IO work namely call a webservice and store the data in a room db. So I was thinking of calling viewModelScope.launch(Dispatchers.IO){ } do all the work on a different thread and in the end update the LiveData result.
viewModelScope.launch(Dispatchers.IO){
liveData.postValue(someRepository.someWork())
}
So my second question is, is this the way to go ?
ViewModelScope.launch { } runs on the main thread, but also gives you the option to run other dispatchers, so you can have UI & Background operations running synchronously.
For you example:
fun thisWillRunOnMainThread() {
viewModelScope.launch {
//below code will run on UI thread.
showLoadingOnUI()
//using withContext() you can run a block of code on different dispatcher
val result = novel.id = withContext(Dispatchers.IO) {
withsomeRepository.someWork()
}
//The below code waits until the above block is executed and the result is set.
liveData.value = result
finishLoadingOnUI()
}
}
For more reference, I would say there are some neat articles that will help you understand this concept.
Medium link that explains it really neat.
So my second question is, is this the way to go ?
I would expect two things to be different in your current approach.
1.) First step would be to define the scheduler of the background operation via withContext.
class SomeRepository {
suspend fun doWork(): SomeResult = withContext(Dispatchers.IO) {
...
}
}
This way, the operation itself runs on a background thread, but you didn't force your original scope to be "off-thread".
2.) Jetpack Lifecycle KTX provides the liveData { coroutine builder so that you don't have to postValue to it manually.
val liveData: LiveData<SomeResult> = liveData {
emit(someRepository.someWork())
}
Which in a ViewModel, you would use like so:
val liveData: LiveData<SomeResult> = liveData(context = viewModelScope.coroutineContext) {
withContext(Dispatchers.IO) {
emit(someRepository.someWork())
}
}
And now you can automatically trigger data-loading via observing, and not having to manually invoke viewModelScope.launch {}.
The idea behind main thread being default is you can run UI operations without having to change the context. It is a convention I guess Kotlin coroutine library writers have chosen
Suppose if by default if the launch runs on IO thread then the code would look like this
viewmodelScope.launch {
val response = networkRequest()
withContext(Dispatchers.Main) {
renderUI(response)
}
}
Suppose if by default if the launch runs on Default thread then the code would look like this
viewmodelScope.launch {
val response: Response = null
withContext(Dispatchers.IO) {
response = networkRequest()
}
withContext(Dispatchers.Main) {
renderUI(response)
}
}
Since the default launch is on main thread, now you have to do below
viewmodelScope.launch {
val response: Response = null
withContext(Dispatchers.IO) {
response = networkRequest()
}
renderUI(response)
}
To avoid the messy code initializing the response with null, we can also make the networkRequest as suspend and wrap the business logic of networkRequest() function in withContext(Dispatchers.IO) and that's how lot of people write their networkRequest() function as well! Hope this makes sense
One of the main reasons it runs on Main thread, is because it's more practical for general use in ViewModel, like murali kurapati wrote. It was a design choice.
It's also important to note that all suspending functions should be "main safe" according to best pracices. That means, that your repository should switch coroutine context accordingly, like so:
class someRepository(private val ioDispatcher: CoroutineDispatcher) {
suspend fun someWork() {
withContext(ioDispatcher) {
TODO("Heavy lifting")
}
}
}
I am new to multithreading and looking for solution for this problem.
I am launching a method in coroutine which updates data in my database and if it is updated I would like to update the UI for users. How to this? I cannot put runOnUiThread inside a coroutine. Is there some type of magic like -> when coroutine finished -> then -> runOnUi?
Greetings
You don't need to call runOnUiThread as the coroutine will have the main dispatcher as the context.
Let's say you have this helper function to offload work to the I/O thread.
suspend fun <T> withIO(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.IO, block)
If you are using a ViewModel, then you can call it like this
viewModelScope.launch {
val result = withIO {
// You are on IO thread here.
update your database
}
// The block will be suspended until the above task is done.
// You are on UI thread now.
// Update your UI.
}
If you are not using a ViewModel, you can also use
withContext(Disptachers.Main) {
val result = withIO {
// You are on IO thread
}
// You are back on the main thread with the result from the task
}
Coroutine are task that work on different thread.
What you really want is wating for changes in database. Coroutine in this idea could work for insert data in db, but listening part is role of ViewModel pattern.
I recently answer similar question to yours:
AutocompleteTextView with room
More specific could be this answer from another user:
Wait until Kotlin coroutine finishes in onCreateView()
So the basic problem is to jumping back to main thread after co-routine finishes
this can be done multiple ways
using launch(Dispatcher.Main)
from main thread init co-routine
something like this
//launches coroutine running on main thread
GlobalScope.launch(Dispatchers.Main) {
updateDb()
}
suspend fun updateDb(){
//runs on worker thread and returns data
val value = withContext(Dispatchers.IO){
saveDataInDb();
}
//runs back on main thread
updateUI(value);
}
However global scope should not be used
You can read about that here https://medium.com/#elizarov/the-reason-to-avoid-globalscope-835337445abc
using async await
suspend fun saveInDb() {
val value = GlobalScope.async {
delay(1000)
println("thread running on [${Thread.currentThread().name}]")
10
}
println("value = ${value.await()} thread running on [${Thread.currentThread().name}]")
}
output:
thread running on [DefaultDispatcher-worker-1]
value = 10 thread running on [main]
thread running on [main]
I've recently dove into Kotlin coroutines
Since I use a lot of Google's libraries, most of the jobs is done inside Task class
Currently I'm using this extension to suspend coroutine
suspend fun <T> awaitTask(task: Task<T>): T = suspendCoroutine { continuation ->
task.addOnCompleteListener { task ->
if (task.isSuccessful) {
continuation.resume(task.result)
} else {
continuation.resumeWithException(task.exception!!)
}
}
}
But recently I've seen usage like this
suspend fun <T> awaitTask(task: Task<T>): T = suspendCoroutine { continuation ->
try {
val result = Tasks.await(task)
continuation.resume(result)
} catch (e: Exception) {
continuation.resumeWithException(e)
}
}
Is there any difference, and which one is correct?
UPD: second example isn't working, idk why
The block of code passed to suspendCoroutine { ... } should not block a thread that it is being invoked on, allowing the coroutine to be suspended. This way, the actual thread can be used for other tasks. This is a key feature that allows Kotlin coroutines to scale and to run multiple coroutines even on the single UI thread.
The first example does it correctly, because it invokes task.addOnCompleteListener (see docs) (which just adds a listener and returns immediately. That is why the first one works properly.
The second example uses Tasks.await(task) (see docs) which blocks the thread that it is being invoked on and does not return until the task is complete, so it does not allow coroutine to be properly suspended.
One of the ways to wait for a Task to complete using Kotlin Coroutines is to convert the Task object into a Deferred object by applying Task.asDeferred extension function. For example for fetching data from Firebase Database it can look like the following:
suspend fun makeRequest() {
val task: Task<DataSnapshot> = FirebaseDatabase.getInstance().reference.get()
val deferred: Deferred<DataSnapshot> = task.asDeferred()
val data: Iterable<DataSnapshot> = deferred.await().children
// ... use data
}
Dependency for Task.asDeferred():
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.5.2'
To call suspend function we need to launch a coroutine:
someCoroutineScope.launch {
makeRequest()
}
someCoroutineScope is a CoroutineScope instance. In android it can be viewModelScope in ViewModel class and lifecycleScope in Activity or Fragment, or some custom CoroutineScope instance. Dependencies:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'