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;
    }
附上流程图




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值