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;
}
}
};
16 Comments to Android – Displaying Dialogs From Background Threads
Leave a comment
Search
Archive
| M | T | W | T | F | S | S |
|---|---|---|---|---|---|---|
| « Nov | ||||||
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | ||||
Recent Comments
- wesley on How to sign an unsigned Android package (.apk file)
- dimitar on Android – Displaying Dialogs From Background Threads
- Salmpy on Android – Displaying Dialogs From Background Threads
- Mark Quinn on How to connect your Android phone to Ubuntu to do development, testing, installations or tethering
- Mark Quinn on How to connect your Android phone to Ubuntu to do development, testing, installations or tethering
Categories
Blogroll
Online Tools
Other
BLOG ARCHIVE
- November 2011 (1)
- August 2011 (1)
- April 2011 (1)
- January 2011 (2)
- September 2010 (1)
- August 2010 (2)
- July 2010 (2)
- June 2010 (2)
- May 2010 (1)
- January 2010 (2)
- December 2009 (2)
- November 2009 (3)
- October 2009 (1)
- September 2009 (3)
- July 2009 (1)
- May 2009 (1)
- March 2009 (1)
- February 2009 (2)
- January 2009 (2)
- December 2008 (1)
- November 2008 (4)
- October 2008 (5)
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…
great!
Thanks! Mine is 4 out of 1000 installs to be exact.
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
@Michael, bingo – just started having the same issue after switching to tabs.
Many thanks!
Thanks man!!
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
Thank you Man
Thanks a lot man. Works for me =D
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);
}
}
}
}
}
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();
Perfect! Thanks.
In my case error occurred because the dialog show was invoked during screen rotation. I will put your check.
Thanks
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
@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.