Android – Displaying Dialogs From Background Threads

Having threads to do some heavy lifting and long processing in the background is pretty standard stuff. Very often you would want to notify or prompt the user after the background task has finished by displaying a Dialog.

The displaying of the Dialog has to happen on the UI thread, so you would do that either in the Handler object for the thread or in the onPostExecute method of an AsyncTask (which is a thread as well, just an easier way of implementing it). That is a textbook way of doing this and you would think that pretty much nothing wrong could go with this.

Surprisingly I found out that something CAN actually go wrong with this. After Google updated the Android Market and started giving crash reports to the developers I received the following exception:

android.view.WindowManager$BadTokenException: Unable to add window — token android.os.BinderProxy@447a6748 is not valid; is your activity running?
at android.view.ViewRoot.setView(ViewRoot.java:468)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
at android.view.Window$LocalWindowManager.addView(Window.java:424)
at android.app.Dialog.show(Dialog.java:239)
at android.app.Activity.showDialog(Activity.java:2488)

at android.os.Handler.dispatchMessage(Handler.java:99)

I only got a couple of these exceptions from thousands of installs, so I knew that was not anything that happens regularly or that it was easy to replicate.

Looking at the stack trace above it gives us a pretty good idea why it failed. It started in the Handler object, which naturally was called by a background thread after it finished its processing. The Handler instance tried to show a Dialog and before it could show it, it tried to set the View for it and then it failed with:

android.view.WindowManager$BadTokenException: Unable to add window — token android.os.BinderProxy@447a6748 is not valid; is your activity running?

The 447a6748 number is just a memory address of an object that no longer exists.

Note- do not get hung up on the exact number. It would be different with every execution.

Now we know why the application crashed, the only thing left is to figure out what caused it?

We know that background threads execute independently of the main UI thread. That means that the user could be interacting with the application during the time that the thread is doing its work under the covers. Well, what happens if the user hits the “Back” button on the device while the background thread is running and what happens to the Dialog that this thread is supposed to show? Well, if the timing is right the application will most likely crash with the above described error.

In other words what happens is that the Activity will be going through its destruction when the background thread finishes its work and tries to show a Dialog.

In this case it is almost certain that this should have been handled by the Virtual Machine. It should have recognized the fact that the Activity is in the process of finishing and not even attempted to show the Dialog. This is an oversight of the Google developers and it will probably be fixed some time in the future, but in the meantime the burden is on us to take care of this.

The fix to this is pretty simple. Just test if the Activity is going through its finishing phase before displaying the Dialog:

private Handler myHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
    switch (msg.what) {
      case DISPLAY_DLG:
        if (!isFinishing()) {
        showDialog(MY_DIALOG);
        }
      break;
    }
  }
};
Friday, August 20th, 2010 Android, Java, Programming

16 Comments to Android – Displaying Dialogs From Background Threads

  1. Hey Thanks….
    I was finishing activity just after the call to AsyncTask thats why exception was getting fired.
    coz there was no activity to show dialog.
    anyways thanks for guiding…

  2. shashank degloorkar on March 10th, 2011
  3. great!

  4. wang on May 24th, 2011
  5. Thanks! Mine is 4 out of 1000 installs to be exact.

  6. kn on May 24th, 2011
  7. I’ve found another one. The same message (during testing of course) is reasoned by the fact that I’m running the activity which should start the dialog in a tabbed Host.
    In that case you’ve to write
    xyz = new Dialog (getParent()) instead of
    new Dialog (this)

    regards

    Michael

  8. Michael on July 11th, 2011
  9. @Michael, bingo – just started having the same issue after switching to tabs.

  10. Artem Russakovskii on August 9th, 2011
  11. Many thanks!

  12. Diana on August 19th, 2011
  13. Thanks man!!

  14. Demc on September 29th, 2011
  15. Thank you for this information. I put it into work just now. I hope this helps the few users that are reporting the crash. How annoying!

    Thank you very much,

    Joshua

  16. Joshua on October 20th, 2011
  17. Thank you Man

  18. iiizio on November 6th, 2011
  19. Thanks a lot man. Works for me =D

  20. Jonas on November 20th, 2011
  21. You could put an extra check, making the activity a WeakReference or your handler a WeakReference. For example, say you have an AsyncTask and you send the handler as parameter:

    public class SomeThread extends AsyncTask<Void, Void, HashMap> {
    private WeakReference weakHandler;

    public SomeThread(WeakReference weakProgressDialog, WeakReference weakHandler) {
    this.weakProgressDialog = weakProgressDialog;
    this.weakHandler = weakHandler;
    }

    @Override
    protected HashMap doInBackground(Void… params) {
    ErrorObject errorObject = null;
    HashMap result = null;
    String webResponse = DoSomeServerRequest();
    if (webResponse != null) {
    errorObject = parseError(webResponse);
    if (errorObject != null) {
    result.put(errorObject, null);
    } else {
    MyObject myObject = parseMyObject(webResponse);
    if (myObject != null) {
    result.put(null, myObject);
    }
    }

    return result;
    }

    errorObject = new ErrorObject();
    errorObject.setCode(0);
    errorObject.setMessage(“An error has occured”);

    result.put(errorObject, null);

    return result;
    }

    @Override
    protected void onPostExecute(HashMap result) {
    ProgressDialog progressDialog = weakProgressDialog.get();
    if (progressDialog != null) {
    progressDialog.dismiss();
    }

    if (result != null) {
    for (Map.Entry resultEntries : result.entrySet()) {
    ErrorObject errorObject = resultEntries.getKey();
    MyObject myObject = resultEntries.getValue();

    Message msg = new Message();
    Bundle b = new Bundle();
    if (errorObject != null) {
    b.putSerializable(“error”, errorObject);
    } else if (domainsList != null) {
    b.putSerializable(“myObject”, myObject);
    }

    msg.setData(b);

    Handler myHandler = weakHandler.get();
    if (myHandler != null) {
    myHandler.sendMessage(msg);
    }
    }
    }
    }
    }

  22. Bogdan on November 22nd, 2011
  23. Sorry, I forgot to mention the usage:

    Handler myHandler = new Handler() {
    // Handler implementation
    };

    ProgressDialog progressDialog = ProgressDialog.show(myContext, “”, “Loading. Please wait…”, true);

    WeakReference weakProgressDialog = new WeakReference(progressDialog);
    WeakReference weakHandler = new WeakReference(myHandler);

    SomeThread someThread = new SomeThread(weakProgressDialog, weakHandler)
    someThread.execute();

  24. Bogdan on November 22nd, 2011
  25. Perfect! Thanks.

  26. Hagar on January 2nd, 2012
  27. In my case error occurred because the dialog show was invoked during screen rotation. I will put your check.
    Thanks

  28. Ivano on January 14th, 2012
  29. Hi. Intesting post. But can u help me? I have two classes: 1 is activity and 2 is non-activity class where i have create different Dialogs. Then i test connection from activity class – all works fine, but when i try to check connection on button back from 3 activity (in theory ot might be bring me back to 1 activity) my dialog dont appear and all app is crashed! Can i use urs code in this case and how? Thanks.
    PS Sorry for my bad English :)

  30. Salmpy on January 30th, 2012
  31. @Salmpy,

    I am not sure I understand what you are trying to do.
    But if you are trying to display dialogs from classes that do not inherit from Activity, you need to pass in an Activity context.

    So basically you can either have a member variable in the non Activity class to hold a reference to your Activity context, or pass the activity context in a method call (as a parameter). If that is what you are looking for and need more explanation, let me know.

  32. dimitar on January 31st, 2012

Leave a comment