昨天做了一個demo,靜態注冊的BroadcastrReceiver在onReceive方法里實現 alertdialog.
但是,健哥說我的這個會報錯,但是為什么沒報錯很奇怪,我也很奇怪,今早一來我就研究了一下alertdialog的坑。
dialog 是類型同activity的應用窗口,都可以創建phonewindow實例。
看看dialog的構造函數:
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { // 忽略一些代碼 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); final Window w = new PhoneWindow(mContext); mWindow = w; w.setCallback(this); w.setOnWindowDismissedCallback(this); w.setWindowManager(mWindowManager, null, null);//就是這句話。 w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler(this); }
setWindowManager(WindowManager wm, IBinder appToken, String appName) 第二個參數,我們設為null了,這個就是token,是個badtoken。(而在activity中,這個token被設為ActivityThread傳過來的token。token呢是用來表示窗口的一個令牌,只有符合條件的token才能被WMS通過添加到應用上。)
在Dialog的show方法中,
public void show() {
// 忽略一些代碼
mDecor = mWindow.getDecorView();
WindowManager.LayoutParams l = mWindow.getAttributes();
// 忽略一些代碼
try {
mWindowManager.addView(mDecor, l);//返回manager的時候,如果tockon不為null會調用getSystemService(),為null,返回windowmanager時,會有一個badtockonexception的檢測,會報出異常。
mShowing = true; sendShowMessage(); } finally { } }
Dialog最終也是通過系統的WindowManager把自己的Window添加到WMS上。在addView前,Dialog的token是null
Dialog初化始時是通過Context.getSystemServer 來獲取 WindowManager,會得到一個WindowManagerImpl的實例,這個實例里token也是null。之后在Dialog的show方法中將Dialog的DecorView(PhoneWindow.getDecorView())添加到WindowManager時會給token設置默認值還是null。返回windowmanager時,會有一個badtockonexception的檢測,會報出異常。
public Object getSystemService(@ServiceName @NonNull String name) { if (getBaseContext() == null) { throw new IllegalStateException( "System services not available to Activities before onCreate()"); }//因為一直傳過來的context的tocken if (WINDOW_SERVICE.equals(name)) { return mWindowManager; } else if (SEARCH_SERVICE.equals(name)) { ensureSearchManager(); return mSearchManager; } return super.getSystemService(name); }
系統對TYPE_APPLICATION類型的窗口,要求必需是Activity的Token,不是的話系統會拋出BadTokenException異常。Dialog 是應用窗口類型,Token必須是Activity的Token。
谷歌為什么要設置這個Token機制呢?
為了防止bad Token啊。
什么是BadToken呢?
引用大神的解釋:
通過Token來驗證WindowManager服務請求方是否是合法的。如果我們可以使用Application的Context,或者說Token可以不是Activity的Token,那么用戶可能已經跳轉到別的應用的Activity界面了,但我們卻可以在別人的界面上彈出我們的Dialog,想想就覺得很危險。
如你跳到了微信界面了,這時在后台的某個應用里調用Dialog的show,那么微信的界面上會顯示一個Dialog,這個Dialog可能會讓用戶輸入密碼什么的,而用戶完全無法區分是不是微信彈出的。