android.view.WindowLeaked 的錯誤問題

gao_chun發表於2014-09-18

09-17 23:03:38.954: E/WindowManager(1994): android.view.WindowLeaked: Activity app.ui.MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{b2d3d4e0 V.E..... R.....I. 0,0-280,139} that was originally added here
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.ViewRootImpl.<init>(ViewRootImpl.java:348)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:248)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.app.Dialog.show(Dialog.java:286)
09-17 23:03:38.954: E/WindowManager(1994): 	at app.ui.MainActivity.showExitDialog(MainActivity.java:69)
09-17 23:03:38.954: E/WindowManager(1994): 	at app.ui.MainActivity.onKeyDown(MainActivity.java:48)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.KeyEvent.dispatch(KeyEvent.java:2640)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.app.Activity.dispatchKeyEvent(Activity.java:2423)
09-17 23:03:38.954: E/WindowManager(1994): 	at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1962)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3418)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3558)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:3718)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2010)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:1704)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:1695)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:1987)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.os.MessageQueue.nativePollOnce(Native Method)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.os.MessageQueue.next(MessageQueue.java:138)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.os.Looper.loop(Looper.java:123)
09-17 23:03:38.954: E/WindowManager(1994): 	at android.app.ActivityThread.main(ActivityThread.java:5017)
09-17 23:03:38.954: E/WindowManager(1994): 	at java.lang.reflect.Method.invokeNative(Native Method)
09-17 23:03:38.954: E/WindowManager(1994): 	at java.lang.reflect.Method.invoke(Method.java:515)
09-17 23:03:38.954: E/WindowManager(1994): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
09-17 23:03:38.954: E/WindowManager(1994): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
09-17 23:03:38.954: E/WindowManager(1994): 	at dalvik.system.NativeStart.main(Native Method)


首先說明:這個錯誤不屬於記憶體溢位,而是android中的窗體溢位。

1.Activity 的生命週期與 Dialog 的不一樣 AlertDialog.Builder builder=new AlertDialog.Builder(context);

2.這個context 是Activity的上下文 

3.Dialog 要在Activity 銷燬前 dismiss()掉,不然就窗體溢位

所以在解決此問題時,要判斷窗體是否關閉,再幹掉Activity

    /**
     * 捕獲返回按鈕事件
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            showExitDialog();
            return false;
        } else {
            return super.onKeyDown(keyCode, event);
        }
    }

    //退出提示
    protected void showExitDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("提示");
        builder.setMessage("您確定要退出麼?");
        builder.setNegativeButton("取消", null);
        builder.setPositiveButton("退出", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                MainActivity.this.finish();
                dialog.cancel();//取消dialog,或dismiss掉
            }
        });
        if (!isFinishing()) {
            builder.create().show();
        }
    }
原因:
Android的每一個Activity都有個WindowManager窗體管理器,同樣,構建在某個Activity之上的對話方塊、PopupWindow也有相應的WindowManager窗體管理器。但是它們不能脫離Activity而單獨存在著,因為需要Activity的Context,所以當某個Dialog或者某個PopupWindow正在顯示的時候我們幹掉了承載該Dialog(或PopupWindow)的Activity,就會拋Window Leaked異常了,因為這個Dialog(或PopupWindow)的WindowManager已經沒有誰可以附屬了(Context)。


在android中常常會遇到與context有關的內容,淺論一下context : 在語句 AlertDialog.Builder builder = new AlertDialog.Builder(this); 中,要求傳遞的引數就是一個context,在這裡傳入的是this,那麼這個this究竟指的是什麼東東呢? 這裡的this指的是Activity.this,是當前這個Activity 的上下文(Context)。

網上有很多朋友在這裡傳入this.getApplicationContext(),這是不對的。為什麼? AlertDialog物件是依賴於一個View的,而View是和一個Activity對應的。 於是,這裡涉及到一個生命週期的問題,this.getApplicationContext()取的是整個應用程式的ContextActivity.this取的是這個Activity的Context,這兩者的生命週期是不同 的,前者的生命週期是整個應用,後者的生命週期只是它所在的Activity。而AlertDialog應該是屬於一個Activity的,在Activity銷燬的時候它也就銷燬了,不會再存在;但是,如果傳入this.getApplicationContext(),就表示它的生命週期是整個應用程式,這顯然超過了它 的生命週期了。 

所以,在這裡只能使用Activity的this。 差別就是不同的生命週期。

相關文章