java.lang.IllegalArgumentException: View not attached to window manager when dismissing dialog

Exception One,
java.lang.IllegalArgumentException: View not attached to window manager
at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:664)
at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:354)
at android.view.WindowManagerImpl$CompatModeWrapper.removeView(WindowManagerImpl.java:165)
at android.app.Dialog.dismissDialog(Dialog.java:319)
at android.app.Dialog.dismiss(Dialog.java:302)
at com.android.email.activity.setup.AccountSetupBasics$FindProviderTask.onPostExecute(AccountSetupBasics.java:914)
at com.android.email.activity.setup.AccountSetupBasics$FindProviderTask.onPostExecute(AccountSetupBasics.java:863)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4780)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:808)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:575)
at dalvik.system.NativeStart.main(Native Method)

Exception Two,
STACK TRACE:
java.lang.IllegalArgumentException: View not attached to window manager
at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:355)
at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:200)
at android.view.Window$LocalWindowManager.removeView(Window.java:432)
at android.app.Dialog.dismissDialog(Dialog.java:280)
at android.app.Dialog.access$000(Dialog.java:73)
at android.app.Dialog$1.run(Dialog.java:109)
at android.app.Dialog.dismiss(Dialog.java:264)
at com.xxxxxxxx.suquashi.ActionDialog.access$001(ActionDialog.java:43)
at com.xxxxxxxx.suquashi.ActionDialog$2.onAnimationEnd(ActionDialog.java:299)
at android.view.animation.AnimationSet.getTransformation(AnimationSet.java:329)
at android.view.ViewGroup.drawChild(ViewGroup.java:1494)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1356)
at android.view.ViewGroup.drawChild(ViewGroup.java:1627)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1356)
at android.view.ViewGroup.drawChild(ViewGroup.java:1627)


[Bad Design]
The following code is a bad design to put dialog.dismiss() and activity.finish() in the asynctask.

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);
 
 finish();
    }
};


First every dialog or sth extends dialog would have a own window and décor view(itself), and except popup(dialog) there is a window and décor view. Second when the phone change orientation, the activity Thread would call HandleRelaunchActivity(this would call handledestroyactivity), the window manager would remove all the window and décor view and then create new window and décor, so the old décor has no use after this had been done.
In the code of action dialog there would be a animation make the super dismiss called later, but if the HandleRelaunchActivity happened, the actiondialog's décor would be useless, then the animation call back come, the super dismiss is called, it should not be in the mview[] in windowmanagerimpl. After that analyze, I think if this happened, we don't have very useful method to prevent, and it should be ok for a not in the array, but if lockview method throw a exception, I think we could just catch it. It won't affect much because the décor view(actiondialog) would be useless.

Change affects the way the ActionDialog behaves when it is dismissed. Most notable for an end user is that an ActionDialog that is closed when its Activity is being shut down will no longer animate.

Addition to the information below The reason for the Dialog being removed from WindowManager in the first place is typically due to a rotation of the screen, and if the ActionDialog is managed the Android system will take care of removing/dismissing it.

The problem seems to be because ActionDialog delays Dialog.dismiss() to play an animation. When the animation is finished ActionDialog calls Dialog.dismiss but at that time it has already been removed from the WindowManager so the WindowManager will throw an exception when Dialog requests to be removed from WindowManager.

This problem is thus related to ActionDialog and not HomeScreen which means that other applications using ActionDialog could also get this problem. I have seen one MTBF-crash for com.sonyericsson.contacts and one for Timescape due to this.

It 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.

[Dialog dismiss Framework Code]

301    public void dismiss() {
302        if (Thread.currentThread() != mUiThread) {
303            mHandler.post(mDismissAction);
304        } else {
305            mHandler.removeCallbacks(mDismissAction);
306            mDismissAction.run();
307        }
308    }

For Exception One, In email, IllegalArgumentException rises in onPostExec when the AsyncTsk is finishing.
When dismissing the dialog, WindowManager can't find the window and view in its Views.   

300    public void dismiss() {
301        if (Looper.myLooper() == mHandler.getLooper()) {
302            dismissDialog();
303        } else {
304            mHandler.post(mDismissAction);
305        }
306    }
307
308    void dismissDialog() {
309        if (mDecor == null || !mShowing) {
310            return;
311        }
312
313        if (mWindow.isDestroyed()) {
314            Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
315            return;
316        }
317
318        try {
319            mWindowManager.removeView(mDecor);
320        } finally {
321            if (mActionMode != null) {
322                mActionMode.finish();
323            }
324            mDecor = null;
325            mWindow.closeAllPanels();
326            onStop();
327            mShowing = false;
328
329            sendDismissMessage();
330        }
331    }

158        @Override
159        public void removeView(View view) {
160   if(null == view)
161   {
162             Log.d("WindowManager", "uuNote:removeView:view=null###");
163    throw new NullPointerException("view parameter is NULL into findViewLocked()");
164   }
165            mWindowManager.removeView(view);
166        }

352    public void removeView(View view) {
353        synchronized (this) {
354            int index = findViewLocked(view, true);
355            View curView = removeViewLocked(index);
356            if (curView == view) {
357                return;
358            }
359
360            throw new IllegalStateException("Calling with view " + view
361                    + " but the ViewAncestor is attached to " + curView);
362        }
363    }

649    private int findViewLocked(View view, boolean required) {
650        synchronized (this) {
651            final int count = mViews != null ? mViews.length : 0;
652            for (int i=0; i<count; i++) {
653                if (mViews[i] == view) {
654                    return i;
655                }
656            }
657            if(null==view)
658            {
659             Log.d("WindowManager", "findViewLocked:view=null###");
660             required = false;
661  throw new NullPointerException("view parameter is NULL into findViewLocked()");
662            }
663            if (required) {
664                throw new IllegalArgumentException(
665                        "View not attached to window manager");
666            }
667            return -1;
668        }
669    }
670}


[Reference Solution]
Move the dialog.dismiss() and activity.finish to UiThread.

 new Thread() {  2946 
  @Override  2947 
  public void run() {  2948 
   Uri uri = ContentUris.withAppendedId(SemcThreads.CONTENT_URI,  2949 
      threadId);  2950 
   getContentResolver().delete(uri, null, null);  2951 
   runOnUiThread(new Runnable() {  2952 
    public void run() {  2953 
     pd.dismiss();  2954 
           2955 
     // prevent saving of draft  2956 
     mComposer = null;  2957 
     mConversation = null;  2958 
           2959 
     Notifications.updateAll(ConversationActivity.this);  2960 
     ConversationCache.clear();  2961 
     finish();  2962 
    }  2963 
   });  2964 
  }  2965 
 }.start();  

if you use many dialogs, should close all dialog in activity's overriden 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.isShowing()) {
        mDialog.dismiss();
    }
}

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

And at last, is it better to use dialog.cancel() rather than dialog.dismiss()?
 

 

posted @ 2013-04-03 17:38  javawebsoa  Views(2322)  Comments(0Edit  收藏  举报