Dialog ParentWindow android.view.WindowManager$BadTokenException源码剖析

08-07 21:26:43.506: ERROR/AndroidRuntime(9390): android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application  
产生问题原因解析:使用getApplicationContext()里面的是不包含token的,正常情况下,Activiy启动过程中会创建IApplicationToken,并传给WindowManager生成AppWIndowToekn,在WindowManagerService.addWindow中会判断appken是否为NULL,其中Dialog的type 为2,而Activity window的type为1。
@WindowManager
 /**
         * Window type: an application window that serves as the "base" window
         * of the overall application; all other application windows will
         * appear on top of it.
         * In multiuser systems shows only on the owning user's window.
         */
        public static final int TYPE_BASE_APPLICATION   = 1;
/**
         * Window type: a normal application window.  The {@link #token} must be
         * an Activity token identifying who the window belongs to.
使用这种类型的window需要一个Activity的token
         * In multiuser systems shows only on the owning user's window.
         */
        public static final int TYPE_APPLICATION        = 2;
错误产生代码如下:
@WindowManagerService.addWindow():
else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                AppWindowToken atoken = token.appWindowToken;//判断是否是一个Activity的apptoken
                if (atoken == null) {
                    Slog.w(TAG, "Attempted to add window with non-application token "
                          + token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } 
....
}
如果Dialog创建的时候使用Activity的context会获得Activity的apptoken,下面来分析获得的过程。
再来说正常情况下Dialog怎么获得Activity的token的:
首先Dialog创建的时候同时会创建一个PhoneWindow();并且持有的WindowManager是context中获取的,也就是Activity对应的ContextImpl
 Dialog(Context context, int theme, boolean createContextThemeWrapper) {
       ....
        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Window w = PolicyManager.makeNewWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.GRAVITY_BY_PARENT);
        mListenersHandler = new ListenersHandler(this);
    }
而在show阶段,最终Dialog的parentWindow是Activity的window,所以dialog和所属的Activity传
public void show() {
     ..... 
      mCanceled = false;
        
        if (!mCreated) {
            dispatchOnCreate(null);
        }

        onStart();
        mDecor = mWindow.getDecorView();

    ......

        WindowManager.LayoutParams l = mWindow.getAttributes();
        if ((l.softInputMode
                & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
            WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
            nl.copyFrom(l);
            nl.softInputMode |=
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
            l = nl;
        }
		l.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
        try {
            mWindowManager.addView(mDecor, l);
            mShowing = true;
    
            sendShowMessage();
        } finally {
        }
    }

关键点在这:Activity重写了getSystemService方法,重写windowmanager和searchmanager的实例。
 @Override
    public Object getSystemService(@ServiceName @NonNull String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }

        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }
通过默认ContextImpl得到的windowmanager和Activity得到的windowmanager有不同,多了一个parentWindow参数:
Activiy在attach的时候会去创建Window,然后调用setWindowManager的方法
final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,int align) {
    .....

        mWindow = PolicyManager.makeNewWindow(this);
        .....

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
     
        mWindowManager = mWindow.getWindowManager();//重写了WindowManager
        .......
}

setWindowManager方法会重新创建一个LocalWindowManager
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mDisplay, parentWindow);
    }

这样用Activity的Context获取的WindowManager在addView的时候,该view所属的window的parentwindow是Activiy的window。后续传给WindowManagerService的token也就是Activiy的token。
重写apptoken的地方在WindowManagerGlobal.addView,这个方法其中有一句
if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } 

void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
        CharSequence curTitle = wp.getTitle();
        if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
            wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            if (wp.token == null) {
                View decor = peekDecorView();
                if (decor != null) {
                    wp.token = decor.getWindowToken();
                }
            }
            if (curTitle == null || curTitle.length() == 0) {
                String title;
                if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) {
                    title="Media";
                } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY) {
                    title="MediaOvr";
                } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
                    title="Panel";
                } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) {
                    title="SubPanel";
                } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) {
                    title="AtchDlg";
                } else {
                    title=Integer.toString(wp.type);
                }
                if (mAppName != null) {
                    title += ":" + mAppName;
                }
                wp.setTitle(title);
            }
        } else {
            if (wp.token == null) {
                wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
            }
            if ((curTitle == null || curTitle.length() == 0)
                    && mAppName != null) {
                wp.setTitle(mAppName);
            }
        }
        if (wp.packageName == null) {
            wp.packageName = mContext.getPackageName();
        }
        if (mHardwareAccelerated) {
            wp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        }
		wp.align = getAttributes().align;
    }
附上流程图




猜你喜欢

转载自blog.csdn.net/xuning2516/article/details/78447206