Update Ui from Thread in Android

Updating Android UI using threads

You cannot touch anything in the UI thread from a background thread, to do that use Handlers, initialize your background thread passing it a Handler object. When data arrives to use the handler to send a message to the UI. In the UI when the message from the background thread comes, just update the Views.

Example Code Snippet :

in the background thread:

if(dataArrives){
Message msg = handler.obtainMessage();
msg.what = UPDATE_IMAGE;
msg.obj = bitmap;
msg.arg1 = index;
handler.sendMessage(msg);
}

in the UI thread:

final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what==UPDATE_IMAGE){
images.get(msg.arg1).setImageBitmap((Bitmap) msg.obj);
}
super.handleMessage(msg);
}
};

and pass the handler to the background thread.

Problem with updating UI with background thread

Undoubtedly, you should use different thread for heavy processes in java. But, Anything related to UI changes, initializations like Views, Animations should be done in UI(Main) Thread which is created with your application.

You could also do:

 new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap =
processBitMap("image.png");

new Handler.post(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap);
}
});


}

Even if you do a background job in another thread, Handler guarantees, setting image is done in UI/Main thread.

Most efficient way to continuously update the UI Thread in Android

You can post runnable on the thread that updates the view:

clock.post(new Runnable() {
@Override public void run() {
clock.setText(convertTime());
// 50 millis to give the ui thread time to breath. Adjust according to your own experience
clock.postDelayed(this, 50);
}
});

(I omitted the stopping logic to keep the example short. Putting it back is a matter of testing and not reposting the runnable).

Why is it possible to update UI using Handler created in a background Thread?

When you connect a Handler to your UI thread, the code that handles
messages runs on the UI thread.

taken from the docs

i believe because you're calling the workerThread.startTask() from the button click, which is the UI thread, the handler is simply returning the result to the UI thread as well, which is why the UI will update as well

How to update UI thread from coroutines background thread?

You can either switch dispatcher contexts (Dispatchers.IO for the logic, then to Dispatchers.Main for updating the UI), or you can move your code into a ViewModel and there use the same context switching technique or use postvalue() of LiveData. An example of doing the latter below. You can read on ViewModel here: https://developer.android.com/topic/libraries/architecture/viewmodel

import androidx.lifecycle.LiveData 
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class MyViewModel() : ViewModel() {
private val files: MutableLiveData<List<String>> by lazy {
MutableLiveData<List<String>>()
}
fun loadFiles(path: String) {
viewModelScope.launch(){
doLoadFiles()
}
}
private suspend fun doLoadFiles() {
withContext(Dispatchers.IO) {
val results = listOf("patha", "pathb")//replace with your actual code
files.postValue(results)
}
}
fun getFiles(): LiveData<List<String>> = files
}

Then call it like this from your activity

import androidx.appcompat.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders

import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val model = ViewModelProviders.of(this)[MyViewModel::class.java]
model.getFiles().observe(this, Observer<List<String>>{ paths ->
// update UI
println (paths)
})
model.loadFiles("S")

}

In your build.gradle file, make sure to import the relevant dependencies

 def lifecycle_ver = "2.2.0-rc02"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_ver"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_ver"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_ver"

Android-RxJava: Update UI from background thread using observable

With AsyncTask, you're basically performing an asynchronous operation on a worker thread, then using its result on the main thread. In Rx you'd use something like the following:

Observable.fromCallable(asyncOperation)
.subscribeOn(backgroundThread)
.observeOn(mainThread)
.subscribe(result -> { /* update UI for instance */ })

It seems you're also interested in onNext, onError and onComplete.

  • onNext is called every time the observable emits an item. Each time it's called it receives an item, and can then process it.
  • onError is called when the observable has encountered an error for whatever reason. When it's called, it receives a Throwable, which represents the cause of the error. after it's called, onNext and onComplete are not called.
  • onComplete is called after onNext is called with the last item. It doesn't receive any input, you could do some clean up in it for example.

Using the above methods looks like this:

Observable.fromCallable(asyncOperation)
.subscribeOn(backgroundThread)
.observeOn(mainThread)
.subscribe(onNext, onError, onComplete)

[Edit]

If you'd like to create your Observable using Observable.create(), you can definitely do that, it gives you finer control over what and when you emit through the Observable. You can do this for instance if you want to handle some specific errors that can result from your network request, and emit different Throwables depending on the error.

ObservableOnSubscribe asyncOperation = new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> emitter) {
try {
// network request
// Once result is ready, call emitter.onNext().
// When done, complete this Observable by calling emitter.onComplete()
} catch (Exception e) {
// handle error, and emit it using emitter.onError()
}
}
}


Related Topics



Leave a reply



Submit