Perry Hoekstra
Perry Hoekstra

Reputation: 2763

Accessing UI Thread from Async Thread

Quick question: I have been using frameworks that spawn worker threads to perform asynchronous tasks, a good example is Retrofit. Within the success/failure sections, I may pop up a Dialog box which would need to be on the UI thread. I have been accessing the underlying Activity/UI thread in this fashion within the success/failure sections of Retrofit:

Dialog dialog = new Dialog(LoginActivity.this, R.style.ThemeDialogCustom);

This works well 99.9% of the time but every once in a while, I receive the following error when creating a Dialog box:

android.view.WindowManager$BadTokenException
LoginActivity.java line 343 in LoginActivity$6.success()
Unable to add window -- token android.os.BinderProxy@41662138 is not valid;
is your activity running?

So, is my approach the most stable way to access the Activity context/UI thread from a worker thread or do I need a different approach?

Upvotes: 2

Views: 1633

Answers (2)

Swayam
Swayam

Reputation: 16354

AFAIK, there is nothing wrong with the approach you are using. The problem is occurring because the by the time the worker thread finishes and you are trying to show the dialog, the instance of the Activity has finished. So, the crash is totally dependent on the amount of time it takes for the thread to finish. And it seems that in your case, the thread mostly finishes when the Activity is still active; hence you don't get the error is most cases.

What you need to do is to check if the Activity is still running before trying to show the Dialog. One of the simplest ways would be to

if(!((Activity) LoginActivity.this).isFinishing())
{
    //safe to show your dialog
}

Upvotes: 0

Patrick
Patrick

Reputation: 35224

If you work with threads and not using Asynctasks, always run everything that changes UI in runOnUIThread like this

activity.runOnUiThread(new Runnable() {
     @Override
     public void run() {
         //change UI
     }
});

The more generic way to do it is this, which is pretty much the same

new Handler(Looper.getMainLooper()).post(new Runnable() {
         @Override
         public void run() {
             //change UI
         }
    })

See here the minimal difference between runOnUIThread and MainLooper

If you want to check if you are on the main/ui thread

if(Thread.currentThread() == Looper.getMainLooper().getThread()) {
   //you are on the main thread
}

Upvotes: 2

Related Questions