AlertDialog 的context 不能是application的context


昨天做了一個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可能會讓用戶輸入密碼什么的,而用戶完全無法區分是不是微信彈出的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM