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 Throwable
s 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
Get Only Email Address from Contact List Android
How to Enable Disable Gps Programmatically in Android
How to Change the Spinner Background in Android
How to Get the Screensize Programmatically in Android
Have Both Gms and Hms in the Project
How to Change the Textcolor on an Android Searchview
Securityexception: Caller Uid Xxxx Is Different Than the Authenticator's Uid
Android Studio:How to Uninstall APK (Or Execute Adb Command) Automatically Before Run or Debug
How to Fix "Unexpected Element <Queries> Found in <Manifest>" Error
Xamarin.Forms Listview: Set the Highlight Color of a Tapped Item
Monodroid: Error When Calling Constructor of Custom View - Twodscrollview
Changing Background Color of Selected Item in Recyclerview
Android Studio - Android Emulator Wifi Connected with No Internet
How to Use the Firebase Server Timestamp to Generate Date Created
Enable/Disable Zoom in Android Webview
Edittext: Disable Paste/Replace Menu Pop-Up on Text Selection Handler Click Event