Reputation: 543
I have a bunch of AsyncTasks which fetch data. They update the progress as they are executing by displaying dialog boxes. Sometimes this causes the app to crash when the app is in the background (not visible). My code called in below. What checks can I do to keep the app from crashing without re-factoring the dialog box entirely?
These are declared in DownloadAsyncTask and passed in on constructor
protected ProgressDialog mDialog;
protected String mDialogMessage = "Loading Data";
protected ProgressBar progBar;
doInBackground() {
if ( mProgressBar != null && mProgressBar.getVisibility() == View.INVISIBLE) {
mUIThreadHandler.post(new Runnable() {
@Override
public void run() {
mProgressBar.setVisibility(View.VISIBLE);
}
});
} else if ( mDialog != null && !mDialog.isShowing() ) {
mUIThreadHandler.post(new Runnable() {
@Override
public void run() {
mDialog.setMessage(mDialogMessage);
mDialog.show();
}
});
}
}
and then onPostExecute()
:
@Override
protected void onPostExecute(DefaultHandler handler) {
super.onPostExecute(handler);
if ( progBar != null && progBar.getVisibility() == View.VISIBLE ) {
progBar.setVisibility(View.INVISIBLE);
} else if ( mDialog != null && mDialog.isShowing() ) {
try {
mDialog.dismiss();
} catch (Exception e) {
}
}
}
UPDATED CRASH LOG:
12-20 15:19:15.802: E/AndroidRuntime(9529):
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@41a37908 is not valid; is your activity running? 12-20 15:19:15.802: E/AndroidRuntime(9529): at android.view.ViewRootImpl.setView(ViewRootImpl.java:567) 12-20 15:19:15.802: E/AndroidRuntime(9529): at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:246) 12-20 15:19:15.802: E/AndroidRuntime(9529): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69) 12-20 15:19:15.802: E/AndroidRuntime(9529): at android.app.Dialog.show(Dialog.java:281) 12-20 15:19:15.802: E/AndroidRuntime(9529): at com.*.DownloadAsyncTask$2.run(DownloadAsyncTask.java:119) 12-20 15:19:15.802: E/AndroidRuntime(9529): at android.os.Handler.handleCallback(Handler.java:725) 12-20 15:19:15.802: E/AndroidRuntime(9529): at android.os.Handler.dispatchMessage(Handler.java:92) 12-20 15:19:15.802: E/AndroidRuntime(9529): at android.os.Looper.loop(Looper.java:137) 12-20 15:19:15.802: E/AndroidRuntime(9529): at android.app.ActivityThread.main(ActivityThread.java:5039) 12-20 15:19:15.802: E/AndroidRuntime(9529): at java.lang.reflect.Method.invokeNative(Native Method) 12-20 15:19:15.802: E/AndroidRuntime(9529): at java.lang.reflect.Method.invoke(Method.java:511) 12-20 15:19:15.802: E/AndroidRuntime(9529): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run
Upvotes: 1
Views: 1158
Reputation: 716
you should check that activity is not finished.
use this:
if (!isFinishing()) {
mDialog.show();
}
check this page for more description:
http://dimitar.me/android-displaying-dialogs-from-background-threads/
Upvotes: 1
Reputation: 721
Use runOnUiThread(runnable) to avoid crash: this essentially runs the code segment in the run method on the UI thread. wrap all the UI operations you would want to perform in the run method as show below.
so the code would go something like:
yourActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
// whatever UI operations..
..
mDialog.setMessage(mDialogMessage);
mDialog.show();
}
});
Upvotes: 0
Reputation: 32026
You are touching the UI from the background thread. Android technically says "do not update", but it is best to read that as "do not interact with the UI on background threads". Your if
statement essentially says "if (...) is true about the UI, post this task to be executed on the UI thread at some time in the future". By the time your Runnable
executes on the UI thread, the UI's state may have changed. It is probably better to encapsulate the entire logic and ship that to UI thread to be executed. You could do that by combining the two Runnable
s you have, and shipping in a single post
, but AsyncTask
was designed so you would not need to deal with handlers directly. If you call publishProgress()
in doInBackGround it will post a call on the UI thread to the onProgressUpdate()
member. Therefore, the easier thing to do is combine the code in your Runnable
s and move to the onProgressUpdate()
method.
Something like --
void onProgressUpdate() {
if (mProgressBar != null && mProgressBar.getVisibility() == View.INVISIBLE) {
mProgressBar.setVisibility(View.VISIBLE);
} else if (mDialog != null && !mDialog.isShowing()) {
mDialog.setMessage(mDialogMessage);
mDialog.show();
}
}
Upvotes: 1