159

I have an activity that starts AsyncTask and shows progress dialog for the duration of operation. The activity is declared NOT be recreated by rotation or keyboard slide.

    <activity android:name=".MyActivity" 
              android:label="@string/app_name"
              android:configChanges="keyboardHidden|orientation"
              >
        <intent-filter>
        </intent-filter>
    </activity>

Once task completed, I dissmiss dialog, but on some phones (framework: 1.5, 1.6) such error is thrown:

java.lang.IllegalArgumentException: View not attached to window manager
    at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:356)
    at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:201)
    at android.view.Window$LocalWindowManager.removeView(Window.java:400)
    at android.app.Dialog.dismissDialog(Dialog.java:268)
    at android.app.Dialog.access$000(Dialog.java:69)
    at android.app.Dialog$1.run(Dialog.java:103)
    at android.app.Dialog.dismiss(Dialog.java:252)
    at xxx.onPostExecute(xxx$1.java:xxx)

My code is:

final Dialog dialog = new AlertDialog.Builder(context)
    .setTitle("Processing...")
    .setCancelable(true)
    .create();

final AsyncTask<MyParams, Object, MyResult> task = new AsyncTask<MyParams, Object, MyResult>() {

    @Override
    protected MyResult doInBackground(MyParams... params) {
        // Long operation goes here
    }

    @Override
    protected void onPostExecute(MyResult result) {
        dialog.dismiss();
        onCompletion(result);
    }
};

task.execute(...);

dialog.setOnCancelListener(new OnCancelListener() {
    @Override
    public void onCancel(DialogInterface arg0) {
        task.cancel(false);
    }
});

dialog.show();

From what I have read (http://bend-ing.blogspot.com/2008/11/properly-handle-progress-dialog-in.html) and seen in Android sources, it looks like the only possible situation to get that exception is when activity was destroyed. But as I have mentioned, I forbid activity recreation for basic events.

So any suggestions are very appreciated.

alex2k8
  • 42,496
  • 57
  • 170
  • 221

15 Answers15

234

I too get this error sometimes when I dismiss dialog and finish activity from onPostExecute method. I guess sometimes activity gets finished before dialog successfully dismisses.

Simple, yet effective solution that works for me

@Override
protected void onPostExecute(MyResult result) {
    try {
        if ((this.mDialog != null) && this.mDialog.isShowing()) {
            this.mDialog.dismiss();
        }
    } catch (final IllegalArgumentException e) {
        // Handle or log or ignore
    } catch (final Exception e) {
        // Handle or log or ignore
    } finally {
        this.mDialog = null;
    }  
}
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Damjan
  • 2,933
  • 1
  • 19
  • 10
  • I got the same message when my activity was finished and OnCancelled() was called in my task, where I did dismiss the dialog. Think you are right. – jellyfish May 16 '11 at 12:25
  • 46
    Simple solution? Yes. Effective? Perhaps in this case. Would I recommend it? NO! Don't swallow **ALL** exceptions like that! I would not even catch the IllegalArgumentException, but look for another solution. – Simon Forsberg Jan 25 '13 at 11:11
  • 6
    Because usually empty try-catches is a bad idea... Although it sometimes can be the right thing to do. – Thomas Mar 13 '13 at 11:55
  • @Damjan Your answer has been copied by someone here http://stackoverflow.com/questions/16188961/progress-dialogue-box-giving-exception-in-asynchtask see the answer by gowtham. – Nikhil Agrawal Apr 24 '13 at 11:02
  • 3
    @Damjan By your response you suggest catch type Exception. Well, this is a bad practice by Google. You can read about it here: [Don't Catch Generic Exception](http://source.android.com/source/code-style.html#dont-catch-generic-exception). – Yaniv Jun 06 '13 at 07:23
  • 18
    I believe this an effective fix. In general cases, we should not do this, but since the Android Framework does not provide any easy check for us, we have to use unusual way. Also, if a dialog's isShowing() call working as we expect, we do not need this kind of hack. – SXC Aug 21 '13 at 20:10
  • @hogun's solution worked for me. I was already checking to see if the dialog was not null and if it was showing before I would dismiss it and then set it to null, but it would still generate the IllegalArgumentException. For me, this only appeared to happen when I would rotate the device very quickly. Running the same check in `onDestroy` to see if the dialog was not null and showing, and then closing it and setting it to null fixed the problem for me. – Jason Sep 25 '13 at 14:53
  • I don't agree with you. `try{}catch()` don't be a best way to do at all. That is a `java.lang.IllegalArgumentException` so we need to know why's that. – Trần Đức Tâm Jun 04 '14 at 04:11
  • 1
    quick fix until something better is found – Rohit Tigga Sep 03 '14 at 04:23
  • 1
    Has anyone found a viable alternative to this solution? – Rohit Tigga Sep 19 '14 at 02:48
  • 1
    Omg what is this answer ... So now for each java issue, try{}catch{Throwable} !! – amdev Dec 07 '14 at 18:41
  • @Damjan is it safe not to use this.mDialog.dismiss? Just put mDialog.dismiss instead – stuckedunderflow Feb 20 '15 at 16:31
  • Halim sure, code you're seeing was edited by Jared to reflect good dev practices. I don't find it practical though :) I'd do it like this: ` @Override protected void onPostExecute(MyResult result) { try { dialog.dismiss(); dialog = null; } catch (Exception e) { // nothing } finish(); } ` – Damjan Feb 22 '15 at 07:52
  • Swallowing an exception without addressing the real issue is not helpful at all. – Knuckles the Echidna May 21 '16 at 22:26
  • For me it worked for original phone when connected. When tested in emulator android this exception threw. I wiped the data and cold booted android emulator and this worked! – Parthan_akon Aug 16 '21 at 06:08
  • In Android 7 its is working, latest android 10 and 11 it is not. – Parthan_akon Aug 16 '21 at 06:36
18

Here is my "bullet proof" solution, which is compilation of all good answers that I found on this topic (thanks to @Damjan and @Kachi). Here the exception is swallowed only if all other ways of detection did not succeeded. In my case I need to close the dialog automatically and this is the only way to protect the app from crash. I hope it will help you! Please, vote and leave comments if you have remarks or better solution. Thank you!

public void dismissWithCheck(Dialog dialog) {
        if (dialog != null) {
            if (dialog.isShowing()) {

                //get the Context object that was used to great the dialog
                Context context = ((ContextWrapper) dialog.getContext()).getBaseContext();

                // if the Context used here was an activity AND it hasn't been finished or destroyed
                // then dismiss it
                if (context instanceof Activity) {

                    // Api >=17
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        if (!((Activity) context).isFinishing() && !((Activity) context).isDestroyed()) {
                            dismissWithTryCatch(dialog);
                        }
                    } else {

                        // Api < 17. Unfortunately cannot check for isDestroyed()
                        if (!((Activity) context).isFinishing()) {
                            dismissWithTryCatch(dialog);
                        }
                    }
                } else
                    // if the Context used wasn't an Activity, then dismiss it too
                    dismissWithTryCatch(dialog);
            }
            dialog = null;
        }
    }

    public void dismissWithTryCatch(Dialog dialog) {
        try {
            dialog.dismiss();
        } catch (final IllegalArgumentException e) {
            // Do nothing.
        } catch (final Exception e) {
            // Do nothing.
        } finally {
            dialog = null;
        }
    }
Ivo Stoyanov
  • 16,256
  • 8
  • 62
  • 65
13

Here is the correct solution to solving this problem:

public void hideProgress() {
    if(mProgressDialog != null) {
        if(mProgressDialog.isShowing()) { //check if dialog is showing.

            //get the Context object that was used to great the dialog
            Context context = ((ContextWrapper)mProgressDialog.getContext()).getBaseContext();

            //if the Context used here was an activity AND it hasn't been finished or destroyed
            //then dismiss it
            if(context instanceof Activity) { 
                if(!((Activity)context).isFinishing() && !((Activity)context).isDestroyed()) 
                    mProgressDialog.dismiss();
            } else //if the Context used wasnt an Activity, then dismiss it too
                mProgressDialog.dismiss();
        }
        mProgressDialog = null;
    }
}

Instead of blindly catching all exceptions, this solution addresses the root of the problem: trying to dimiss a dialog when the activity used to initialize the dialog has already been finished. Working on my Nexus 4 running KitKat, but should work for all versions of Android.

Kachi
  • 3,609
  • 2
  • 23
  • 18
  • 3
    `isDestroyed` requires API 17+ – Androiderson Apr 03 '15 at 19:36
  • Why do you need to set mProgressDialog to null? Is that related with memory leak? Could you please explain? – Pawan Feb 19 '16 at 07:27
  • 1
    @Pawan, it's an implementation detail on my end. It's not required, it's just the way the function in this class works. After a progress dialog is hidden, I set it to null. When a user wants to show another progress dialog, a new instance is instantiated. – Kachi Feb 20 '16 at 08:04
  • Definitely !((Activity)context).isFinishing() is required, thanks! :) – Daniel Krzyczkowski Aug 25 '16 at 15:47
13

I may have a workaround.

Was having the same issue, where I am loading lots of items (via the file system) into a ListView via an AsyncTask. Had the onPreExecute() firing up a ProgressDialog, and then both onPostExecute() and onCancelled() (called when the task is cancelled explicitly via AsyncTask.cancel()) closing it via .cancel().

Got the same "java.lang.IllegalArgumentException: View not attached to window manager" error when I was killing the dialog in the onCancelled() method of the AsyncTask (I'd seen this done in the excellent Shelves app).

The workaround was to create a public field in the AsyncTask that contains the ProgressDialog:

public ProgressDialog mDialog;

Then, in onDestroy() when I cancel my AsyncTask, I can also kill the associated dialog via:

AsyncTask.mDialog.cancel();

Calling AsyncTask.cancel() DOES trigger onCancelled() in the AsyncTask, but for some reason by the time that method is called, the View has already been destroyed and thus cancelling the dialog is failing.

Druid
  • 6,423
  • 4
  • 41
  • 56
Unpossible
  • 10,607
  • 22
  • 75
  • 113
  • I find the UserTask implementation just excellent as @Paul mentioned. Source code is here: http://code.google.com/p/shelves/source/browse/trunk/Shelves/src/org/curiouscreature/android/shelves/util/UserTask.java – Evi Song Apr 14 '13 at 05:25
  • While the use case can be found in the same project: http://code.google.com/p/shelves/source/browse/trunk/Shelves/src/org/curiouscreature/android/shelves/activity/ShelvesActivity.java#613 – Evi Song Apr 14 '13 at 05:26
7

i agree a opinion of 'Damjan'.
if you use many dialogs, should close all dialog in onDestroy() or onStop().
then you may be able to reduce the frequency 'java.lang.IllegalArgumentException: View not attached to window manager' exception occurs.

@Override
protected void onDestroy() {
    Log.d(TAG, "called onDestroy");
    mDialog.dismiss();
    super.onDestroy();
}



but little exceed...
to make it more clear, you prevent to show any dialog after onDestroy called.
i don't use as below. but it's clear.

private boolean mIsDestroyed = false;

private void showDialog() {
    closeDialog();

    if (mIsDestroyed) {
        Log.d(TAG, "called onDestroy() already.");
        return;
    }

    mDialog = new AlertDialog(this)
        .setTitle("title")
        .setMessage("This is DialogTest")
        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        })
        .create();
    mDialog.show();
}

private void closeDialog() {
    if (mDialog != null) {
        mDialog.dismiss();
    }
}

@Override
protected void onDestroy() {
    Log.d(TAG, "called onDestroy");
    mIsDestroyed = true;
    closeDialog();
    super.onDestroy();
}


good luck!

Hogun
  • 602
  • 6
  • 9
  • I'll always prefer avoiding empty catch blocks. worth a try, but since this error is hard to produce - only time will tell if it's actually working. thanks anyway. – Dror Fichman Mar 13 '13 at 10:33
  • what is empty catch blocks? i don't use try/catch. a mIsDestroyed variable is exceeded working. but if you write to code that dialog show after some working in other thread, you might need this variable. when other thread is working, if activity is finished, you can look at this exception. – Hogun Mar 27 '13 at 09:57
  • I had the same prob and when i added @Override public void onPause(){ if(dialog != null) dialog.dismiss(); super.onPause(); } till present i dont have this error ... so I think its the same like your answer and its really useful – Chris Sim Jun 20 '14 at 11:57
  • @ChrisSim hello! onPuase() and onDestroy() is defference. when Activity is onPuase, Dialog is closed. and when you execute the app, Dialog isn't shown. do you want to it? – Hogun Jul 19 '15 at 03:11
  • @Hogun Yes of course, I mean same idea, I'm closing the dialog on pause instead on destroy because I need it in on pause not on destroy. Secondly, I closing it just when it's not null. Thank you for explaining this for others. – Chris Sim Jul 20 '15 at 06:54
  • There is a nice answer to a similar question that provides some more context on the problem and a similar solution. https://stackoverflow.com/a/23586127/1271867 – Tad Aug 22 '17 at 13:12
4

Use this.

if(_dialog!=null && _dialog.isShowing())
_dialog.dismiss();
Mick MacCallum
  • 129,200
  • 40
  • 280
  • 281
Pralabh Jain
  • 137
  • 3
  • 7
  • 2
    It's almost the same solution that @Damjan proposed. – Yury Sep 26 '12 at 15:18
  • 28
    This is not enough, the IllegalArgumentException still happens with this check. – Murphy Oct 29 '12 at 17:14
  • I did same solution, but still not know if it is effective. Only difference, i nested two if's to be sure that second part .isShowing() will not be evaluated if is null. – Nick Jul 18 '13 at 17:17
  • 2
    This is not sufficient. – trante Dec 20 '13 at 18:12
  • 1
    @Nick: There is no need to nest multiple `if`'s in cases like this, Java's `&&` operator has lazy evaluation (also called short-circuiting), which means the 2nd operand is not evaluated if the first one evaluates to `false` (which means the result of the `&&` will always be `false` anyway, hence "lazy" evaluation). Similarly `||` will not evaluate its 2nd operand if the first one evaluates to `true`. Note: the `&` and `|` operators do not have this behaviour and thus always evaluate both operands. – Matthias Mar 16 '15 at 14:33
3

I had the same problem, you can solve it by:

@Override
protected void onPostExecute(MyResult result) {
    try {
        if ((this.mDialog != null) && this.mDialog.isShowing()) {
            this.mDialog.dismiss();
        }
    } catch (final IllegalArgumentException e) {
        // Handle or log or ignore
    } catch (final Exception e) {
        // Handle or log or ignore
    } finally {
        this.mDialog = null;
    }  
}
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
spacebiker
  • 3,777
  • 4
  • 30
  • 49
2

What worked for me most of the time is to verify whether the Activity is not finishing.

if (!mActivity.isFinishing()) {
    dialog.dismiss();
}
Herve Thu
  • 1,207
  • 1
  • 12
  • 10
2

The activity is declared NOT be recreated by rotation or keyboard slide.

Just got the same problem. Fix for API level 13 or higer.
From Android docs:

Note: If your application targets API level 13 or higher (as declared by the minSdkVersion and targetSdkVersion attributes), then you should also declare the "screenSize" configuration, because it also changes when a device switches between portrait and landscape orientations.

So i've changed my manifest to this:

<activity
        android:name="MyActivity"
        android:configChanges="orientation|screenSize"
        android:label="MyActivityName" >
</activity>

And now it works fine. Activity is not recreating when i rotate the phone, progress dialog and view stay the same. No error for me.

Shunt
  • 179
  • 8
  • this is not a solution. for example I have and Adview activity from admob and it should recreate the activity for the size changes. – Emil Sep 21 '18 at 23:40
2

I think your code is correct unlike the other answer suggested. onPostExecute will run on the UI thread. That's the whole point of AsyncTask - you don't have to worry about calling runOnUiThread or deal with handlers. Furthermore, according to the docs, dismiss() can be safely called from any thread (not sure they made this the exception).

Perhaps it's a timing issue where dialog.dismiss() is getting called after the activity is no longer displayed?

What about testing what happens if you comment out the setOnCancelListener and then exit the activity while the background task is running? Then your onPostExecute will try to dismiss an already dismissed dialog. If the app crashes you can probably just check if the dialog is open before dismissing it.

I'm having the exact same problem so I'm going to try it out in code.

Brandon O'Rourke
  • 24,165
  • 16
  • 57
  • 58
  • I also looked into dimiss() code, and indeed, it can be safely called from any thread. BTW, I have a problem with testing, as this issue happens on users phones, and I was never able to reproduce by myself :-( So trying to figure out by analyzing code... According to timing. I was thinking on this, but can't imagine a situation how the Activity can be closed before Dialog. If BACK pressed, than it will cancel Dialog first. And the activity automatic recreation is forbidden by manifest file, but may be it still can be recreated some how? Let me know if you find some thing! – alex2k8 May 14 '10 at 10:55
2

First of all do error handling where ever you trying to dismiss the dialog.

 if ((progressDialog != null) && progressDialog.isShowing()) {
            progressDialog.dismiss();
            progressDialog = null;
        }

If that doesn't fix then dismiss it in onStop() Method of the activity.

 @Override
    protected void onStop() {
        super.onStop();
        if ((progressDialog != null) && progressDialog.isShowing()) {
            progressDialog.dismiss();
            progressDialog = null;
        }
    }
Amit Patel
  • 1,795
  • 16
  • 20
2

alex,

I could be wrong here, but I suspect that multiple phones 'in the wild' have a bug that causes them to switch orientation on applications that are marked as statically oriented. This happens quite a bit on my personal phone, and on many of the test phones our group uses (including droid, n1, g1, hero). Typically an app marked as statically oriented (perhaps vertically) will lay itself out for a second or two using a horizontal orientation, and then immediately switch back. End result is that even though you don't want your app to switch orientation, you have to be prepared that it may. I don't know under what exact conditions this behavior can be reproduced, I don't know if it is specific to a version of Android. All I know is that I have seen it happen plenty of times :(

I would recommend using the solution provided in the link you posted that suggests overriding the Activity onCreateDialog method and letting the Android OS manage the lifecycle of your Dialogs. It looks to me like even though you don't want your activity to switch orientations, it is switching orientation somewhere. You can try to track down a method that will always prevent orientation switching, but I am trying to tell you that I personally don't believe there is a foolproof way that works on all current Android phones in the market.

Hamy
  • 20,662
  • 15
  • 74
  • 102
  • 1
    You can keep your device from switching orientations, but there are a bunch of other config changes that destroy/recreate your Activity - a common one is sliding a keyboard in or out. – MaximumGoat Jul 26 '12 at 19:57
1

I had the same problem while using a button to sync a list from the server: 1) I click the button 2) A progress dialog shows up while dowloading the list from server 3) I turn the device to another orientation 4) java.lang.IllegalArgumentException: View not attached to window manager on postExecute() of the AsyncTask during progress.dismiss().

As I tried the fix I figured that even if the problem doesn't occur my list wasn't showing all items.

I figured that what I wanted was for the AsyncTask to finish (and dismiss the dialog) before the activity beign destroyed, so I made the asynctask object an attribute and overrided the onDestroy() method.

If the asynctask takes a lot of time the user maybe will feel that the device is slow, but I think that's the price he pays for trying to change the device orientation while the progress dialog is showing up. And even if it takes some time the app doesn't crash.

private AsyncTask<Boolean, Void, Boolean> atask;

@Override
protected void onDestroy() {
    if (atask!=null)
        try {
            atask.get();
        } catch (InterruptedException e) {
        } catch (ExecutionException e) {
        }
    super.onDestroy();
}
shadowglas
  • 51
  • 7
1
@Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);

            if (progressDialog != null && progressDialog.isShowing()) {
                Log.i(TAG, "onPostexucte");
                progressDialog.dismiss();
}
}
vikseln
  • 458
  • 6
  • 8
  • 3
    While this code snippet may answer the question providing some explanation of how it solves the problem will help future visitors to the site understand your answer – RobV Sep 02 '14 at 10:00
0

Migh below code works for you, It works for me perfectly fine:

private void viewDialog() {
    try {
        Intent vpnIntent = new Intent(context, UtilityVpnService.class);
        context.startService(vpnIntent);
        final View Dialogview = View.inflate(getBaseContext(), R.layout.alert_open_internet, null);
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_DIM_BEHIND,
                PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL;
        windowManager.addView(Dialogview, params);

        Button btn_cancel = (Button) Dialogview.findViewById(R.id.btn_canceldialog_internetblocked);
        Button btn_okay = (Button) Dialogview.findViewById(R.id.btn_openmainactivity);
        RelativeLayout relativeLayout = (RelativeLayout) Dialogview.findViewById(R.id.rellayout_dialog);

            btn_cancel.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Handler handler = new Handler(Looper.getMainLooper());
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                if (Dialogview != null) {
//                                ( (WindowManager) getApplicationContext().getSystemService(WINDOW_SERVICE)).removeView(Dialogview);
                                    windowManager.removeView(Dialogview);
                                }
                            } catch (final IllegalArgumentException e) {
                                e.printStackTrace();
                                // Handle or log or ignore
                            } catch (final Exception e) {
                                e.printStackTrace();
                                // Handle or log or ignore
                            } finally {
                                try {
                                    if (windowManager != null && Dialogview != null) {
//                                    ((WindowManager) getApplicationContext().getSystemService(WINDOW_SERVICE)).removeView(Dialogview);
                                        windowManager.removeView(Dialogview);
                                    }
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                            //    ((WindowManager) getApplicationContext().getSystemService(WINDOW_SERVICE)).removeView(Dialogview);
//                        windowManager.removeView(Dialogview);


                        }
                    });
                }
            });
            btn_okay.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Handler handler = new Handler(Looper.getMainLooper());
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            //        ((WindowManager) getApplicationContext().getSystemService(WINDOW_SERVICE)).removeView(Dialogview);
                            try {
                                if (windowManager != null && Dialogview != null)
                                    windowManager.removeView(Dialogview);
                                Intent intent = new Intent(getBaseContext(), SplashActivity.class);
                                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//                        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
//                        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);


                                context.startActivity(intent);
                            } catch (Exception e) {
                                windowManager.removeView(Dialogview);
                                e.printStackTrace();
                            }
                        }
                    });
                }
            });
        } catch (Exception e) {
            //` windowManager.removeView(Dialogview);
            e.printStackTrace();
        }
    }

Do not define your view globally if u call it from background service.

Shashwat Gupta
  • 876
  • 9
  • 22