Change layout from Coroutine - android

Coroutine launching
GlobalScope.launch(){
get_message_pulling()
}
I need to edit the layout from get_message_pulling(), but getting the error
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

because using GlobalScope.launch() without specifying the coroutine context will run your code on a background thread using Dispatchers.Default, not the MainThread, you can only communicate with UI through the MainThread coroutine's context
GlobalScope.launch(){
get_message_pulling()
withContext(Dispatchers.Main) {
// then update the UI
}
}

Solved by
private fun get_message_pulling() {
runOnUiThread {
// ui changing code
}
}

You can use the Android specific Dispatchers.Main for performing UI updates. Also, avoid the use of GlobalScope for launching coroutines as explained here. Instead, opt for Dispatchers.Default or Dispatchers.IO.
// CPU bound - Dispatchers.Default
// IO bound - Dispatchers.IO
val defaultScope = CoroutineScope(Dispatchers.Default)
defaultScope.launch {
get_message_pulling()
withContext(Dispatchers.Main){
// Your UI updates
}
}

Related

Kotlin Android - correct way to dispatch to main thread

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.

Kotlin: lag in coroutine runBlocking

I am using kotlin Coroutines to perform async network operations to avoid NetworkOnMainThreadException.
The problem is the lag that happens when i use runBlocking,that take sometime to complete current thread.
How can i prevent this delay or lag,and allow the async operation to be done without delay
runBlocking {
val job = async (Dispatchers.IO) {
try{
//Network operations are here
}catch(){
}
}
}
By using runBlocking you are blocking the main thread until the coroutine finishes.
The NetworkOnMainThread exception is not thrown because technically the request is done on a background thread, but by making the main thread wait until the background thread is done, this is just as bad!
To fix this you could launch a coroutine, and any code that depends on the network request can be done inside the coroutine. This way code may still be executed on the main thread, but it never blocks.
// put this scope in your activity or fragment so you can cancel it in onDestroy()
val scope = MainScope()
// launch coroutine within scope
scope.launch(Dispachers.Main) {
try {
val result = withContext(Dispachters.IO) {
// do blocking networking on IO thread
""
}
// now back on the main thread and we can use 'result'. But it never blocked!
} catch(e: Exception) {
}
}
If you don't care about the result and just want to run some code on a different thread, this can be simplified to:
GlobalScope.launch(Dispatchers.IO) {
try {
// code on io thread
} catch(e: Exception) {
}
}
Note: if you are using variables or methods from the enclosing class you should still use your own scope so it can be cancelled in time.

Android multithreading - coroutine and UI thread

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]

Execute hard task in Background Thread, return result in Main Thread

I spent some time to find a developer friendly solution (without adding dependencies to the project) of how to perform some hard task in background thread and after the task is completed return result to main thread. I found "AsyncTask" which allows to do that. But to use it you need to write boilerplate code for each task you need to run in Background. I am iOS developer who decided to try Android-related developing. So in Swift you can simply use the next code to make this task:
DispatchQueue.global().async(execute: {
//Do some hard task in background
DispatchQueue.main.async(execute: {
//Return to main
})
})
This looks pretty simple. But in Kotlin I didn't find such simple solution and decided to create it.
Here is what I made:
I created Generic class
import android.os.AsyncTask
class BaseAsyncTask<M>: AsyncTask<()->M, Int, M>() {
var completion: ((M)->Unit)? = null
override fun doInBackground(vararg params: (() -> M)?): M? {
for (p in params) {
return p?.invoke()
}
return null
}
override fun onPostExecute(result: M) {
super.onPostExecute(result)
completion?.invoke(result)
}
}
And Manager
class AsyncManager {
companion object {
fun <M>execute(inBackground: ()->M, inMain: (M)->Unit): BaseAsyncTask<M> {
val task = BaseAsyncTask<M>()
task.completion = inMain
task.execute(inBackground)
return task
}
fun <M>execute(inBackground: ()->M): BaseAsyncTask<M> {
val task = BaseAsyncTask<M>()
task.execute(inBackground)
return task
}
}
}
Now I use it like this:
AsyncManager.execute({
//Do some hard task in background
}, {
//Return to main
})
Looks developer friendly.
Log.e("MAIN", "MAIN THREAD SHOULD NOT BE BLOCKED")
AsyncManager.execute({
Log.e("TASK", "Started background task")
val retval = "The value from background"
Thread.sleep(5000)
Log.e("TASK", "Finished background task with result: " + retval)
retval
}, {
Log.e("TASK", "Started task in Main thread with result from Background: " + it)
})
Log.e("MAIN", "MAIN THREAD SHOULD NOT BE BLOCKED - 1")
And the log:
2019-03-27 17:11:00.719 17082-17082/com.test.testapp E/MAIN: MAIN
THREAD SHOULD NOT BE BLOCKED
2019-03-27 17:11:00.722 17082-17082/com.test.testapp E/MAIN: MAIN
THREAD SHOULD NOT BE BLOCKED - 1
2019-03-27 17:11:00.722 17082-17124/com.test.testapp E/TASK: Started
background task
2019-03-27 17:11:05.737 17082-17124/com.test.testapp E/TASK: Finished
background task with result: The value from background
2019-03-27 17:11:05.738 17082-17082/com.test.testapp E/TASK: Started
task in Main thread with result from Background: The value from
background
So the question is what professional Android developers think about this solution. What problem can I get in case I'll use it. And maybe there is some reason not to use this solution.
If you're using Kotlin, the correct way to do this is via Coroutines, which would allow you to write code such as:
// Launch a coroutine that by default goes to the main thread
GlobalScope.launch(Dispatchers.Main) {
// Switch to a background (IO) thread
val retval = withContext(Dispatchers.IO) {
Log.e("TASK", "Started background task")
val retval = "The value from background"
Thread.sleep(5000)
Log.e("TASK", "Finished background task with result: " + retval)
retval
}
// Now you're back the main thread
Log.e("TASK", "Started task in Main thread with result from Background: " + retval)
}
Note that Kotlin coroutines operate under structured concurrency, so you'd generally want to avoid using GlobalScope and instead scope your coroutine to be tied to your Activity / Fragment lifecycle. This generally needs to be done yourself right now.
ianhanniballake's answer is correct, but is perhaps a bit incomplete, so I figured I'd provide a full generic example.
build.gradle(:app):
dependencies { // this line is probably already present
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"
}
A global CoroutineScope is not bound to any job.
GlobalScope is used
to launch top-level coroutines which are operating on the whole
application lifetime and are not cancelled prematurely. Application
code usually should use an application-defined CoroutineScope.
Using
async or launch on the instance of GlobalScope is highly discouraged.
taken from here
So you want to use any class with a lifecycle as CoroutineScope, so that when it dies, it takes the running background tasks with it to the grave. Often, people recommend to use an activity for this. However, there is a case to be made that you don't want any external class to use your activity as their CoroutineScope, so you can use a protected field instead:
protected val scope = CoroutineScope(Job() + Dispatchers.Main)
At the time of writing, I do not know why we have to create a Job() here. What I do know is that the + operator is overloaded to merge these two context into one. For the Dispatcher part, you can choose a reasonable one. The options include
Dispatchers.Main for the UI thread
Dispatchers.Default for a pool of background threads
Dispatchers.IO for blocking operations that are I/O intensive
Dispatchers.Unconfined for when you really know what you are doing. This article discourages its use "normally".
Now with all this out of the way, the code becomes surprisingly simple:
import kotlin.coroutines.*
// ...
myButton.setOnClickListener() { v: View? ->
myButton.setColorToYellow() // some UI thread work
scope.launch(Dispatchers.Default) {
val result = longComputation() // some background work
withContext(Dispatchers.Main) {
// some UI thread work for when the background work is done
root.findViewById<TextView>(R.id.text_home).text = "Result: $result"
}
}
myButton.setColorToRed() // more UI thread work. this is done instantly
}
Of course, this can be done anywhere - I'm just using a button and an onClickListener to give an example with a possible use case.

Switching to UI context in coroutines

I'm new to coroutines and I'm wondering if it's possible to switch from coroutineScope (GlobalScope) to UI scope for the code below. My problem is that the steps inside the coroutine launch body must be executed in a worker thread, otherwise the listener notification must be executed in the ui thread in order to avoid to call runOnUiThread in my activity code.
override suspend fun startRent(name: String, bikeMode: BikeMode, listener: StartRentListener) {
var bleDevice : RxBleDevice
val scanFilter: ScanFilter = ScanFilter.Builder().setDeviceName(name).build()
val scanSettings: ScanSettings = ScanSettings.Builder().build()
val job = GlobalScope.launch {
try {
bleDevice = rxBleClient.scanBleDevicesExt(rxBleClient, scanSettings, scanFilter)
val bleConnection = bleDevice.establishConnectionExt()
// write handshake
connectionManager.writeHandshake(bleDevice, bleConnection)
// open lock
openLock(bleDevice, bikeMode, bleConnection)
// getting user position
apiHelper.sendLockRequest(bleDevice.name, getPosition())
bleDevice.disconnect()
// this should be called on main thread once all the previous operations are finished
listener.onSuccess()
} catch (e: Exception) {
listener.onError(e)
}
}
job.join()
}
A snippet of my current activity code:
bikeAccessClient.startRent(bikeBLEName, BikeMode.HYBRID, object :
StartRentListener {
override fun onSuccess() {
runOnUiThread {
// UI update here
}
}
You may use withContext(Dispatchers.Main) {..} function to execute a part of your code with the other Coroutine Dispatcher.
kotlinx.coroutines.android contains the definition of the Dispatchers.Main function and it integrates correctly with Android UI.
Using explicit Dispatcher in your code is quite error-prone. Instead, I would recommend designing the code with fewer explicit requirements.
I would wrote something like that:
fun uiActionHandlerToStartTheProcess() {
launch(Dispatchers.Main) {
val result = startRent(...) // no callback here, suspend function
//UI Update Here
}
}
suspend fun CoroutineScope.startRent() : SomeResultOfWork {
//that function offloads the execution to a IO (aka brackground) thread
return withContext(Dispatchers.IO){
//here goes your code from `startRent`
//use `suspendCancellableCoroutine {cont -> .. }` if you need to handle callbacks from it
SomeResultOfWork()
}
The code in the launch(Dispatchers.Main){..} block is executed in the UI thread. The call to startRent suspend function suspends the execution in the UI thread. Once the startRent is ready with the reply (from a background thread) it resumes the execution (which is done by the Dispatchers.Main and equivalent to the runOnUiThread {...}) and executes the UI update from the right thread

Categories

Resources