android筆記之關於java.lang.IllegalArgumentException: View not attached to window manager 錯誤的分析

今天遇到一個很奇特的問題,當用戶設置了PIN碼,在鎖屏界面正常解鎖PIN碼後,進入Launcher時顯示com.Android.phone 已停止運行。一開始猜想會不會是解鎖PIN碼的時候處理導致了Phone進程報錯,通過log分析找到了問題的大概原因:

[plain] view plain copy
  1. AndroidRuntime: FATAL EXCEPTION: main  
  2. AndroidRuntime: java.lang.IllegalArgumentException: View not attached to window manager  
  3. AndroidRuntime:     at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:385)  
  4. AndroidRuntime:     at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:287)  
  5. AndroidRuntime:     at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:79)  
  6. AndroidRuntime:     at android.app.Dialog.dismissDialog(Dialog.java:323)  
  7. AndroidRuntime:     at android.app.Dialog.dismiss(Dialog.java:306)  
  8. AndroidRuntime:     at com.android.stk.StkDialogActivity$4.onClick(StkDialogActivity.java:188)  
  9. AndroidRuntime:     at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:169)  
  10. AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:99)  
  11. AndroidRuntime:     at android.os.Looper.loop(Looper.java:153)  
  12. AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:5299)  
  13. AndroidRuntime:     at java.lang.reflect.Method.invokeNative(Native Method)  
  14. AndroidRuntime:     at java.lang.reflect.Method.invoke(Method.java:511)  
  15. AndroidRuntime:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)  
  16. AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)  
  17. AndroidRuntime:     at dalvik.system.NativeStart.main(Native Method)  


轉載請務必註明出處:http://blog.csdn.net/yihongyuelan

       通過分析PIN碼的解鎖流程知道,並不是PIN碼解釋時的dialog導致的問題。PIN碼解鎖流程另用文檔說明。
仔細查看log後發現有:
[plain] view plain copy
  1. AndroidRuntime:     at com.android.stk.StkDialogActivity$4.onClick(StkDialogActivity.java:188)  
       這是開機識別到SIM卡之後彈出的STK對話框。那爲什麼會報com.android.phone已停止運行呢?

       經過以上分析,我們可以大致猜測是因爲STK引起的問題,既然是STK的問題那爲什麼會報phone的錯誤呢?如果是phone的錯誤那麼從log中應該可以檢索到phone相關的問題,爲什麼沒有呢?那麼我們接下來一一解答這些問題。

(1). 爲什麼報錯com.android.phone已停止運行?

       通過查看STK的源碼(單卡MTK的代碼在/mediatec/packages/app/stk1),在其AndroidManifest.xml可以發現:
android:sharedUserId="android.uid.phone"
也就是STK和Phon在一個進程中,因此在報錯的表現上來講就會是com.andorid.phone。

(2). 爲什麼會報View not attached to window manager錯誤?

       這個錯誤的意思是說我們所操作的View沒有被納入window manager的管理。
       我們知道所有的窗口創建和管理都是依附於window manager的,因此Dialog的創建也不例外。Dialog的創建流程通過查看源碼可以知道,在Dialog的構造函數中,創建了一個Window對象,但我們知道Window對象並不是用於顯示的,真正用於顯示的是View對象。因此通過Dialog的show方法構造了一個mDecor的View對象,並最終通過WindowManager的addView()方法顯示Dialog。
       通過查看log信息我們可以看到com.android.stk.StkDialogActivity$4.onClick(StkDialogActivity.java:188)
查看對應的StkDialogActivity代碼後發現,在188行處代碼爲dialog.dismiss();
       在網絡上搜索後發現,多數情況下出現這種錯誤,都是在dismiss Dialog時,發現創建該Dialog的Activity存在而導致的。
比如在界面上顯示一個Dialog,當任務處理結束後再Dismiss Dialog。如果在Dialog顯示期間,該Activity因爲某種原因被殺掉且又重新啓動了,那麼當任務結束時,Dismiss Dialog的時候WindowManager檢查,就會發現該Dialog所屬的Activity已經不存在了(重新啓動了一次,是一個新的Activity),所以會報IllegalArgumentException: View not attached to window manager.
通過以上分析我們可以知道在STK Dialog在執行dismiss方法時,發現啓動它的Activity已經不見了,被殺掉了(現在這個是重新啓動的),所以才報錯出現異常。

(3). 爲什麼STKDialogActivity會被"殺掉"?

       通過跟蹤查看異常log我們可以看到StkDialogActivity的生命週期打印log如下:
[plain] view plain copy
  1. onCreate  
  2. onResume - mbSendResp[false], sim id: 0  
  3. ... ...省略部分  
  4. onSaveInstanceState  
  5. onPause, sim id: 0  
  6. ... ...省略部分  
  7. onDestroy-  
  8. onCreate  
  9. ... ...省略部分  
  10. onRestoreInstanceState - [com.android.internal.telephony.cat.TextMessage@41fe7d80]  
  11. onResume - mbSendResp[false], sim id: 0  
到這裏後就出現了異常錯誤……
       通過log我們可以很清楚的看到該Activity啓動了兩次。在log中我們也看到了onSaveInstanceState方法。
對於onSaveInstanceState方法,在Android SDK裏面有這樣的描述:
Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action (such as pressing the BACK key)
也就說當某個activity變得“容易”被系統銷燬時,該activity的onSaveInstanceState就會被執行,除非該activity是被用戶主動銷燬的,例如當用戶按BACK鍵的時候。
       注意這裏的容易二字,當前Activity並沒有被銷燬,只是系統覺得它有可能會被銷燬因此會執行該方法。在該方法中我們可以保存Activity中的各種數據信息,如果該Activity真的被殺掉而又重新啓動後,可以使用onRestoreInstanceState方法在重新啓動該Activity時,還原我們之前保存的數據信息。onSaveInstanceState方法的調用遵循一個重要原則,即當系統“未經你許可”銷燬了你的Activity時,onSaveInstanceState就會被系統調用,這是系統的責任,因爲它必須要提供一個機會讓你保存你的數據(當然如果你自己不保存,那就沒法恢復了)。
       因此通過以上分析我們可以看到STKDialogActivity的確被殺掉後再次啓動了,但爲什麼會被殺掉,通過log並沒有找到答案。

(4). 在STKDialogActivity被殺掉時,Dialog存在麼?

       可能大家會有疑問,Dialog都沒有看到,就出現錯誤了,怎麼能確定該Dialog當時一定是顯示的呢?繼續在log中搜索我們可以發現:
[plain] view plain copy
  1. WindowManager: Activity com.android.stk.StkDialogActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{41fea228 V.E..... R.....ID 0,0-1026,433} that was originally added here  
  2. WindowManager: android.view.WindowLeaked: Activity com.android.stk.StkDialogActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{41fea228 V.E..... R.....ID 0,0-1026,433} that was originally added here  
  3. WindowManager:  at android.view.ViewRootImpl.<init>(ViewRootImpl.java:409)  
  4. WindowManager:  at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:218)  
  5. WindowManager:  at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)  
  6. WindowManager:  at android.app.Dialog.show(Dialog.java:281)  
  7. WindowManager:  at android.app.AlertDialog$Builder.show(AlertDialog.java:951)  
  8. WindowManager:  at com.android.stk.StkDialogActivity.onCreate(StkDialogActivity.java:192)  
  9. WindowManager:  at android.app.Activity.performCreate(Activity.java:5122)  
  10. WindowManager:  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1081)  
  11. WindowManager:  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2270)  
  12. WindowManager:  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2358)  
  13. WindowManager:  at android.app.ActivityThread.access$600(ActivityThread.java:156)  
  14. WindowManager:  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1340)  
  15. WindowManager:  at android.os.Handler.dispatchMessage(Handler.java:99)  
  16. WindowManager:  at android.os.Looper.loop(Looper.java:153)  
  17. WindowManager:  at android.app.ActivityThread.main(ActivityThread.java:5299)  
  18. WindowManager:  at java.lang.reflect.Method.invokeNative(Native Method)  
  19. WindowManager:  at java.lang.reflect.Method.invoke(Method.java:511)  
  20. WindowManager:  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)  
  21. WindowManager:  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)  
  22. WindowManager:  at dalvik.system.NativeStart.main(Native Method)  
       這裏的WindowManager報錯是什麼意思呢?這段log是WindowManager拋出的error錯誤,當我們的Dialog還沒有dismiss時,如果此時該Activity被銷燬了,那麼就會出現以上錯誤,提示窗口泄漏(leaked window)。這裏自己也做了一個實驗,寫一個demo,在Activity的onCreate方法中顯示一個Dialog,然後直接調用finish方法。代碼大致如下:
[java] view plain copy
  1. protected void onCreate(Bundle savedInstanceState) {  
  2.     super.onCreate(savedInstanceState);  
  3.     setContentView(R.layout.activity_main);  
  4.     AlertDialog.Builder info = new Builder(this);  
  5.     info.setTitle("Dialog").setPositiveButton("OK"null).setMessage("This is a Dialog");;  
  6.     info.show();   
  7.     finish();  
  8. }  
       直接將此程序run到模擬器或者真機上,查看log我們就能看到leaked window的報錯信息。因此這也證明了前面我們的假設,即StkActivity在被銷燬時,其所依附的Dialog是存在的。

(5). 如何解決這個問題呢?

       通過以上分析之後我們知道了問題出現的原因,那麼如何解決呢?可以通過以下兩個方面來解決:

1. 使用Activity自帶的Dialog控制方法

       在Activity中需要使用對話框,可以使用Activity自帶的回調,比如onCreateDialog(),showDialog(),dimissDialog(),removeDialog()等等。畢竟這些都是Activity自帶的方法,所以用起來更方便,也不用顯示創建和操控Dialog對象,一切都由框架操控,相對來說比較安全。

2. 限制Dialog的生命週期

       讓創建的Dialog對象的存活週期跟Activity的生命週期一致,也就是說Dialog的生命週期被限定在Activity的onCreate()和onDestroy()方法之間。(在onDestroy()中添加dialog.dismiss即可)。



出處:http://blog.csdn.net/yihongyuelan/article/details/9829313
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章