上一篇講了如何使用dialog,這一篇講一講使用dialog show()和cancel()常見的異常,也就是一個dialog使用時機的問題。
由於dialog在新建的時候其實都傳了一個Contex上下文進去,一般我們都是傳使用這個dialog的那個activity的上下文。
1 mPd = new ProgressDialog(this);
也就是其實這一個dialog是和傳入的activity綁定着的,所以如果activity已經finish了或者Destroy了,我們再調用dialog的show()或者cancel():
1、finish之后你的dialog其實已經不合activity綁定了,所以會報not attached to window manager
2、由於activity已經不再使用,而你的dialog還引用着這個activity使用,所以會報 Activity xxxxxActivity has leaked
出這個問題經常的場景就是比如,后台的asynctask在activity已經退出了還在繼續下載,或者自己的操作,操作完了回來還想更新一下dialog通知用戶,但是此時dialog對應的activity已經不存在了。。。。
使用dialog和上下文狀態或者說時機相關的異常:
dismis或者cancel的時候報了not attached to window manager
12-01 13:54:09.375: E/AndroidRuntime(18146): java.lang.IllegalArgumentException: View=com.android.internal.policy.PhoneWindow$DecorView{b414dcc V.E...... R.....ID 0,0-1026,450} not attached to window manager
12-01 13:54:09.375: E/AndroidRuntime(18146): at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:424)
12-01 13:54:09.375: E/AndroidRuntime(18146): at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:350)
12-01 13:54:09.375: E/AndroidRuntime(18146): at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:116)
12-01 13:54:09.375: E/AndroidRuntime(18146): at android.app.Dialog.dismissDialog(Dialog.java:362)
12-01 13:54:09.375: E/AndroidRuntime(18146): at android.app.Dialog.dismiss(Dialog.java:345)
12-01 13:54:09.375: E/AndroidRuntime(18146): at android.app.Dialog.cancel(Dialog.java:1199)
12-01 13:54:09.375: E/AndroidRuntime(18146): at com.decent.decentdialog.MainActivity$ShowTaskAfterFinish.onProgressUpdate(MainActivity.java:85)
12-01 13:54:09.375: E/AndroidRuntime(18146): at com.decent.decentdialog.MainActivity$ShowTaskAfterFinish.onProgressUpdate(MainActivity.java:1)
12-01 13:54:09.375: E/AndroidRuntime(18146): at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:671)
12-01 13:54:09.375: E/AndroidRuntime(18146): at android.os.Handler.dispatchMessage(Handler.java:102)
12-01 13:54:09.375: E/AndroidRuntime(18146): at android.os.Looper.loop(Looper.java:148)
12-01 13:54:09.375: E/AndroidRuntime(18146): at android.app.ActivityThread.main(ActivityThread.java:5417)
12-01 13:54:09.375: E/AndroidRuntime(18146): at java.lang.reflect.Method.invoke(Native Method)
12-01 13:54:09.375: E/AndroidRuntime(18146): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
12-01 13:54:09.375: E/AndroidRuntime(18146): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
最后報了Activity xxxx has leaked, that was originally added here
12-01 13:51:12.220: E/WindowManager(14101): android.view.WindowLeaked: Activity com.decent.decentdialog.MainActivity has leaked window com.android.internal.policy.PhoneWindow$DecorView{b414dcc V.E...... R....... 0,0-1026,450} that was originally added here
12-01 13:51:12.220: E/WindowManager(14101): at android.view.ViewRootImpl.<init>(ViewRootImpl.java:368)
12-01 13:51:12.220: E/WindowManager(14101): at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:299)
12-01 13:51:12.220: E/WindowManager(14101): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
12-01 13:51:12.220: E/WindowManager(14101): at android.app.Dialog.show(Dialog.java:319)
12-01 13:51:12.220: E/WindowManager(14101): at com.decent.decentdialog.MainActivity.onClick(MainActivity.java:38)
12-01 13:51:12.220: E/WindowManager(14101): at android.view.View.performClick(View.java:5198)
12-01 13:51:12.220: E/WindowManager(14101): at android.view.View$PerformClick.run(View.java:21147)
12-01 13:51:12.220: E/WindowManager(14101): at android.os.Handler.handleCallback(Handler.java:739)
12-01 13:51:12.220: E/WindowManager(14101): at android.os.Handler.dispatchMessage(Handler.java:95)
12-01 13:51:12.220: E/WindowManager(14101): at android.os.Looper.loop(Looper.java:148)
12-01 13:51:12.220: E/WindowManager(14101): at android.app.ActivityThread.main(ActivityThread.java:5417)
12-01 13:51:12.220: E/WindowManager(14101): at java.lang.reflect.Method.invoke(Native Method)
12-01 13:51:12.220: E/WindowManager(14101): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
12-01 13:51:12.220: E/WindowManager(14101): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
12-01 13:51:12.225: E/Surface(14101): getSlotFromBufferLocked: unknown buffer: 0xab4bdd90
12-01 13:51:12.243: D/INFO(14101): canceled()
解決方法
1、對於not attached to window manager,需要在show(),dismiss(),cancel的時候判斷一下當前activity的狀態
在你認為有可能在activity finish之后可能操作dialog的地方對contex添加一個狀態的判斷,如果狀態正確才操作它。這個isFinishing()在用戶沒有調用finish()或者這個activty沒有開始finish流程的時候一直返回false,之后一直返回true.
if(!mContext.isFinishing()){ mDilaog.cancel(); }
在4.2之后有新加一個isDestroyed判斷是否已經destory了也可以,兩者在時間點上有一點點差別,個人認為isFinish就可以保證不會出異常了
if(!mContext.isDestroyed()){ mDilaog.cancel(); }
2、對於Activity xxxx has leaked, that was originally added here
那就需要在onDestory()里面增加調用cancel,因為activity在destory的時候它對應的windowManager回去看當前還有沒有view綁定在上面,發現了這個dialog還在綁定在上面就會報錯。
1 @Override 2 public void onDestroy(){ 3 super.onDestroy(); 4 if ( mDialog!=null && mDialog.isShowing() ){ 5 mDialog.cancel(); 6 } 7 }