填坑之旅 -- android.view.WindowManager$BadTokenException崩潰
此文包含android.view.WindowManager$BadTokenException的4種情形:
1.Unable to add window --token null is not valid; is your activity running
2.Unable to add window --token null is not for an application
3.Unable to add window -- token [email protected] is not valid;is your activity running
4.Unable to add window -- token android.app.LocalActivityManager$LocalActivityRecord @xxx is not valid; is your activity running
情形1.android.view.WindowManager$BadTokenException: Unable to add window --token null is not valid; is your activity running?異常處理。
``` $BadTokenException: Unable to add window --
token null is not valid; is your activity running
E/AndroidRuntime(1412): at android.view.ViewRootImpl.setView(ViewRootImpl.java:538)
...... ```
該異常多見於Popup Window元件的使用中丟擲。
原因:錯誤在PopupWindow.showAtLocation(findViewById(R.id.main), Gravity.BOTTOM,0,0); popwindow必須依附於某一個view,而在oncreate中view還沒有載入完畢,必須要等activity的生命週期函式全部執行完畢,你需要依附的view載入好後才可以執行popwindow。
解決辦法:showAtLocation()函式可以這樣改:
``` //修正後代碼
findviewById(R.id.mView).post(new Runnable() {
@Override
public void run() {
popwindow.showAtLocation(mView, Gravity.CENTER, 0, 0);
}
});
```
總結: PopupWindow必須在某個事件中顯示或者是開啟一個新執行緒去呼叫,不能直接在onCreate方法中顯示一個Popupwindow,否則永遠會有以上的錯誤。
參考:
http://stackoverflow.com/questions/4187673/problems-creating-a-popup-window-in-android-activity
情形2.android.view.WindowManager$BadTokenException: Unable to add window --token null is not for an application ?異常處理。
``` $BadTokenException: Unable to add window --
token null is not for an application
E/AndroidRuntime(1412): at android.view.ViewRootImpl.setView(ViewRootImpl.java:538)
...... ```
該異常多見於AlertDialog元件的使用中丟擲。
``` //拋異常程式碼
new AlertDialog.Builder(getApplicationContext()) //不能用getApplicationContext()
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle("Warnning")
.setPositiveButton("Yes", positiveListener)
.setNegativeButton( "No", negativeListener)
.create().show();
```
原因:導致報這個錯是在於new AlertDialog.Builder(mcontext),雖然這裡的引數是AlertDialog.Builder(Context context),但我們不能使用getApplicationContext()
獲得的Context,而必須使用Activity,因為只有一個Activity才能新增一個窗體。
解決方法:將new AlertDialog.Builder(Context context)中的引數用Activity.this(Activity是你的Activity的名稱)或者getActivity()來填充就可以正確的建立一個Dialog了。
``` //修正後代碼
new AlertDialog.Builder(this) //this可以替換為MainActivity.this或getActivity()
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle("Warnning")
.setPositiveButton("Yes", positiveListener)
.setNegativeButton( "No", negativeListener)
.create().show();
```
參考:
http://stackoverflow.com/questions/20779377/android-custom-dialog-gives-an-error
情形3.android.view.WindowManager$BadTokenException: Unable to add window -- token [email protected] is not valid; is your activity running?異常處理。
``` android.view.WindowManager$BadTokenException: Unable to add window --
token [email protected] is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:698)
......
at dalvik.system.NativeStart.main(Native Method)
```
原因:從錯誤資訊我們也可以明白其原因,此問題根本原因就是由於將要彈出的dialog所要依附的View已經不存在導致的。當介面銷燬後再彈出來;或者介面跳轉時我們的view發生改變,dialog依附的context發生變化或者介面未運行了。
解決方法:介面已經銷燬引起的錯誤就只能判斷介面是否存在然後再彈出了。
``` //修正後代碼
if(!isFinishing()) {
alert.show();
} ```
參考:
https://github.com/VKCOM/vk-android-sdk/issues/21
情形4.android.view.WindowManager$LocalActivityRecord @xxx is not valid; is your activity running? 異常處理。
``` android.view.WindowManager$BadTokenException: Unable to add window --
token [email protected]
is not valid; is your activity running?
```
``` //異常程式碼
TipDialog dialog = new TipDialog(XXX.this) ; ```
原因:因為new對話方塊的時候,引數context 指定成了this,即指向當前子Activity的context。但子Activity是動態建立的,不能保證一直存在。其父Activity的context是穩定存在的,所以有下面的解決辦法。
解決方法:將context替換為getParent()即可。 注意:要建立dialog物件,上下文環境必須是activity,同時若ActivityGroup中巢狀ActivityGroup,巢狀多少就該使用多少個getParent()。
``` //修正後代碼,只有最多一個parent的情形
TipDialog dialog = new TipDialog(getParent()) ; ```
``` //修正後代碼,適用於一個或多個parent的情形
Activity activity = TestActivity.this;
while (activity.getParent() != null) {
activity = activity.getParent();
}
TipDialog dialog = new TipDialog(activity) ;
```
參考:
http://stackoverflow.com/questions/9914195/webview-in-activity-group-crashing-on-dialogs
注:為什麼要使用getParent我們可以從ActivityGroup的內部機制來理解:
TabActivity的父類是ActivityGroup,而ActivityGroup的父類是Activity。
因此從Ams的角度來看,ActivityGroup與普通的Activity沒有什麼區別,其生命週期包括標準的start,stop,resume,destroy等,而且系統中只允許同時允許一個ActivityGroup
.
ActivityGroup內部有一個重要成員變數,其型別為LocalActivityManager
,該類的最大特點在於它可以訪問應用程序的主類,即ActivityThread類。Ams要啟動某個Activity或者贊同某個Activity都是通過ActivityThread類執行的,而LocalActivityManager類就意味著可以通過它來裝載
不同的Activity,並控制Activity的不同的狀態。注意,這裡是裝載,而不是啟動,這點很重要。
所謂的啟動
,一般是指會建立一個程序(如果所在的應用經常還不存在)執行該Activity
裝載
僅僅是指把該Activity作為一個普通類進行載入,並建立一個該類的物件而已,而該類的任何函式都沒有被執行。裝載Activity物件的過程對AmS來講是完全不可見的,那些嵌入的Activity僅僅貢獻了自己所包含的Window視窗而已。而子Activity的不同狀態是通過moveToState來處理的。
所以子Activity不是像普通的Activity一樣,它只是提供Window而已,所以在建立Dialog時就應該使用getParent獲取ActivityGroup真正的Activity,才可以加Dialog加入Activity中。
連結:https://www.jianshu.com/p/4c5fafe08fa7