android.view.WindowManager$BadTokenException崩潰的4種情形

android.view.WindowManager$BadTokenException崩潰的4種情形

參考:https://www.jianshu.com/p/4c5fafe08fa7
最近做一個項目,發現拋異常:
Caused by: android.view.WindowManager$BadTokenException: Unable to add window – token android.os.BinderProxy@8bec11 is not valid; is your activity running?

現總結下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 android.os.BinderProxy@XXX 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.WindowManagerBadTokenException:Unabletoaddwindowtokenandroid.os.BinderProxy@XXXisnotvalid;isyouractivityrunning?android.view.WindowManagerBadTokenException: Unable to add window -- token android.os.BinderProxy@XXX is not valid; is your activity running?異常處理。 android.view.WindowManagerBadTokenException: Unable to add window –
token android.os.BinderProxy@4250d6d8 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();
}
參考:
http://stackoverflow.com/questions/25554279/unable-to-add-window-token-android-os-binderproxy4250d6d8-is-not-valid-is-your

https://github.com/VKCOM/vk-android-sdk/issues/21

情形4.android.view.WindowManagerBadTokenException:Unabletoaddwindowtokenandroid.app.LocalActivityManagerBadTokenException: Unable to add window -- token android.app.LocalActivityManagerLocalActivityRecord @xxx is not valid; is your activity running? 異常處理。
android.view.WindowManagerBadTokenException:Unabletoaddwindowtokenandroid.app.LocalActivityManagerBadTokenException: Unable to add window -- token android.app.LocalActivityManagerLocalActivityRecord@43e5b158
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中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章