Android Developer
Android Developer

Reputation: 543

Using dialog from background crashes app

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

Answers (3)

roghayeh hosseini
roghayeh hosseini

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

stack_ved
stack_ved

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

iagreen
iagreen

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 Runnables 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 Runnables 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

Related Questions