从一个Dialog的创建看Android的窗口机制(下篇)

从一个Dialog的创建看Android的窗口机制(上篇)讲到一个Dialog是如何将自己的布局文件加载到DecorView的,这篇主要讲DecorView如何添加到Window上去,Dialog构造方法中创建好DecorView之后调用mWindowManager.addView(mDecor, l),我们上一篇文章分析了此处的mWindowManager就是Activity的mWindowManager

WindowManagerGlobal.addView

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
           //view是Dialog的DecorView,params是
           //直接new的一个WindowManager.LayoutParams
           //display是显示到哪个屏幕,parentWindow是Activity的PhoneWindow
        //省略一些判断条件
        	.....
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            //调整窗口的Token
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
           		......
            }
        }
        
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
             ......
            int index = findViewLocked(view, false);
            //大于0代表已经添加了此窗口
            if (index >= 0) {
                //mDyingViews里面的View代表正在删除
                if (mDyingViews.contains(view)) {
                    //立马销毁
                    mRoots.get(index).doDie();
                } else {
                   //否则抛出异常,同样的窗口不能重复添加
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
            }
            //如果是子窗口
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    //遍历所有窗口,找到其父窗口
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }
            //创建ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);
            //设置窗口参数
            view.setLayoutParams(wparams);
            //下面三个容器中的ViewRootImpl,View,LayoutParams一一对应的
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            try {
                //添加窗口的核心方法
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

adjustLayoutParamsForSubWindow

这个方法在Android窗口与Token对应策略已经很详细分析过了,主要就是根据窗口的类型,子窗口,应用窗口,系统窗口获取不同的token对象,Dialog的窗口类型从从一个Dialog的创建看Android的窗口机制(上篇)得知它是应用窗口类型的

void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
        ......
        //子窗口
        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();
                }
            }
            ......
            //系统窗口
        } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
                wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
            // We don't set the app token to this system window because the life cycles should be
            // independent. If an app creates a system window and then the app goes to the stopped
            // state, the system window should not be affected (can still show and receive input
            // events)
            ......
        } else {
           //应用窗口
            if (wp.token == null) {
                wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
            }
            ....
        }
    }

我们上篇文章看到Dialog自己是没有token的,所以Dialog的token哪来的呢?这里的mContainer为空,所以Dialog的token就是当前Activity的mAppToken

我们如果使用Application或者service类型的context是无法创建Dialog的,原因是这两种context没有token,Dialog为什么需要token,就是因为创建的Dialog必须隶属于某一个Activity,你不能在别人的应用中弹出Dialog,试想一下如果我们能在后台service中创建Dialog,当用户正在使用其他应用你的service突然弹出一个Dialog,这会让用户无法区分此Dialog是哪个应用弹的,这是很危险的

好了token有了接着后面代码看

root.setView

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
		......
		    //绘制流程,measure,layout,draw
			requestLayout();
		......
		    //将Window添加到WMS
			res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);

}

绘制流程不去看,主要关注窗口的添加

addToDisplay

mWindowSession在ViewRootImpl构造方法中创建,用来和WMS通信,同时ViewRootImpl构造方法中还创建了一个W对象,这也是个Binder对象,通过addToDisplay传递给WMS,供WMS和应用端通信,类似与AMS和ActivityThread之间,双向通信

 @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
            Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
                outInsetsState);
    }

WMS.addWindow

addWindow方法中有非常多窗口合法性的判断以及添加窗口权限的判断,先看下对窗口权限的判断

  public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        int[] appOp = new int[1];
        int res = mPolicy.checkAddPermission(attrs, appOp);
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }
        		......
        ......
    }

PhoneWindowManager.checkAddPermission

@Override
    public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
        //获取窗口的类型
        int type = attrs.type;
        outAppOp[0] = AppOpsManager.OP_NONE;
        //如果不是应用窗口,子窗口,系统窗口的一种
        //直接返回无效的类型,Andoird只有三种窗口类型
        if (!((type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW)
                || (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW)
                || (type >= FIRST_SYSTEM_WINDOW && type <= LAST_SYSTEM_WINDOW))) {
            return WindowManagerGlobal.ADD_INVALID_TYPE;
        }
       //如果不是系统窗口则返回OK,不需要检查权限
        if (type < FIRST_SYSTEM_WINDOW || type > LAST_SYSTEM_WINDOW) {
            // Window manager will make sure these are okay.
            return ADD_OKAY;
        }
 		//如果不是SystemAlert类型的窗口,下面这几种属于Alert窗口
 		/*
		 TYPE_PHONE
         TYPE_PRIORITY_PHONE
         TYPE_SYSTEM_ALERT
         TYPE_SYSTEM_ERROR
         TYPE_SYSTEM_OVERLAY
         TYPE_APPLICATION_OVERLAY
		*/
        if (!isSystemAlertWindowType(type)) {
            //这几种系统窗口也不需要检查权限
            switch (type) {
                case TYPE_TOAST:
                	// Only apps that target older than O SDK can add window without a token, after
                    // that we require a token so apps cannot add toasts directly as the token is
                    // added by the notification system.
                    // Window manager does the checking for this.
                    outAppOp[0] = OP_TOAST_WINDOW;
                    return ADD_OKAY;
                case TYPE_DREAM:
                case TYPE_INPUT_METHOD:
                case TYPE_WALLPAPER:
                case TYPE_PRESENTATION:
                case TYPE_PRIVATE_PRESENTATION:
                case TYPE_VOICE_INTERACTION:
                case TYPE_ACCESSIBILITY_OVERLAY:
                case TYPE_QS_DIALOG:
                    return ADD_OKAY;
            }
            //非上面几种类型的窗口需要检查是否具有
            //android.Manifest.permission.INTERNAL_SYSTEM_WINDOW的权限
            //该权限只有系统应用才能获取
            return mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
                    == PERMISSION_GRANTED ? ADD_OKAY : ADD_PERMISSION_DENIED;
        }

        // 走到这里说明添加的窗口是SystemAlert的
        outAppOp[0] = OP_SYSTEM_ALERT_WINDOW;

        final int callingUid = Binder.getCallingUid();
        // 如果是系统应用进程则返回OK,自动授予权限
        if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
            return ADD_OKAY;
        }

        ApplicationInfo appInfo;
        try {
        	//获取当前窗口的应用信息
            appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
                            attrs.packageName,
                            0 /* flags */,
                            UserHandle.getUserId(callingUid));
        } catch (PackageManager.NameNotFoundException e) {
            appInfo = null;
        }
		//如果当前窗口不需要应用或者Android O版本以上并且不是TYPE_APPLICATION_OVERLAY 
		//则也需要检查android.Manifest.permission.INTERNAL_SYSTEM_WINDOW权限
        if (appInfo == null || (type != TYPE_APPLICATION_OVERLAY && appInfo.targetSdkVersion >= O)) {
            return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
                    == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
        }

        //添加的SystemAlert窗口是否被应用允许
        final int mode = mAppOpsManager.checkOpNoThrow(outAppOp[0], callingUid, attrs.packageName);
        switch (mode) {
            case AppOpsManager.MODE_ALLOWED:
            case AppOpsManager.MODE_IGNORED:
                //代表允许
                return ADD_OKAY;
            case AppOpsManager.MODE_ERRORED:
                // 小于Android M的版本则允许否则拒绝
                if (appInfo.targetSdkVersion < M) {
                    return ADD_OKAY;
                }
                return ADD_PERMISSION_DENIED;
            default:
            //检查是否具有android.Manifest.permission.SYSTEM_ALERT_WINDOW权限
                return (mContext.checkCallingOrSelfPermission(SYSTEM_ALERT_WINDOW)
                        == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
        }
    }

权限检查完了接着回到WMS.addWindow

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        int[] appOp = new int[1];
        int res = mPolicy.checkAddPermission(attrs, appOp);
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }

        boolean reportNewConfig = false;
        WindowState parentWindow = null;
        long origId;
        final int callingUid = Binder.getCallingUid();
        //窗口类型
        final int type = attrs.type;
        synchronized(mWindowMap) {
        	//Display没准备好
            if (!mDisplayReady) {
                throw new IllegalStateException("Display has not been initialialized");
            }

            final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
            //Display不存在
            if (displayContent == null) {
                Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                        + displayId + ".  Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            //Display不能访问
            if (!displayContent.hasAccess(session.mUid)
                    && !mDisplayManagerInternal.isUidPresentOnDisplay(session.mUid, displayId)) {
                Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
                        + "does not have access: " + displayId + ".  Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
			//添加的窗口已经存在
            if (mWindowMap.containsKey(client.asBinder())) {
                Slog.w(TAG_WM, "Window " + client + " is already added");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
			//如果是子窗口
            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
            	//找到其父窗口
                parentWindow = windowForClientLocked(null, attrs.token, false);			
                //如果父窗口为空则添加失败
                if (parentWindow == null) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                //如果父窗口也是子窗口则添加失败
                if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
            }
			//如果是虚拟Private显示器窗口并且Display不是Private的则添加失败
            if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
                Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display.  Aborting.");
                return WindowManagerGlobal.ADD_PERMISSION_DENIED;
            }

            AppWindowToken atoken = null;
            //是否有父窗口,我们添加的Dialog不是子窗口没有父窗口
            final boolean hasParent = parentWindow != null;
         	//获取token,如果有父窗口则使用父窗口的token
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
            //如果有父窗口则使用父窗口的类型
            final int rootType = hasParent ? parentWindow.mAttrs.type : type;
			
            boolean addToastWindowRequiresToken = false;
            if (token == null) {
            	//如果token为空并且是应用类型窗口则添加失败
                if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
                    Slog.w(TAG_WM, "Attempted to add application window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果token为空并且是输入法类型窗口则添加失败
                if (rootType == TYPE_INPUT_METHOD) {
                    Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果token为空并且是语音交互类型窗口则添加失败
                if (rootType == TYPE_VOICE_INTERACTION) {
                    Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果token为空并且是壁纸类型窗口则添加失败
                if (rootType == TYPE_WALLPAPER) {
                    Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果token为空并且是屏保类型窗口则添加失败
                if (rootType == TYPE_DREAM) {
                    Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果token为空并且是TYPE_QS_DIALOG类型窗口则添加失败
                if (rootType == TYPE_QS_DIALOG) {
                    Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                 //如果token为空并且是辅助功能覆盖类型窗口则添加失败
                if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
                    Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果token为空并且是TOAST类型窗口
                if (type == TYPE_TOAST) {
                   	//如果版本大于N MR1,则不能随意添加TOAST了
                    if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
                            parentWindow)) {
                        Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
                                + attrs.token + ".  Aborting.");
                        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                    }
                }
                //如果token为空则等于窗口的W对象
                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                //根据token或者W创建WindowToken
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow);
                        //token不为空并且是应用类型窗口
            } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
            	//获取应用的AppWindowToken,AppWindowToken是
            	//WindowToken的子类标识一个Activity窗口
                atoken = token.asAppWindowToken();
                //如果AppWindowToken为空则添加失败
                if (atoken == null) {
                    Slog.w(TAG_WM, "Attempted to add window with non-application token "
                          + token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (atoken.removed) {
                    Slog.w(TAG_WM, "Attempted to add window with exiting application token "
                          + token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_APP_EXITING;
                }
                //如果是输入法类型窗口
            } else if (rootType == TYPE_INPUT_METHOD) {
            	//但是token类型不是输入法则添加失败
                if (token.windowType != TYPE_INPUT_METHOD) {
                    Slog.w(TAG_WM, "Attempted to add input method window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果是语音交互类型窗口
            } else if (rootType == TYPE_VOICE_INTERACTION) {
            	//但是token类型不是语音交互类型则添加失败
                if (token.windowType != TYPE_VOICE_INTERACTION) {
                    Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果是壁纸类型窗口
            } else if (rootType == TYPE_WALLPAPER) {
            	//但是token类型不是壁纸则添加失败
                if (token.windowType != TYPE_WALLPAPER) {
                    Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果是屏保类型窗口
            } else if (rootType == TYPE_DREAM) {
            	//但是token类型不是屏保则添加失败
                if (token.windowType != TYPE_DREAM) {
                    Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果是辅助功能覆盖类型窗口
            } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
            	//但是token类型不是辅助功能覆盖类型则添加失败
                if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
                    Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果是TOAST类型窗口
            } else if (type == TYPE_TOAST) {
                //如果是Android N MR1版本以上则不能随意添加,
                //需要检查token
                addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
                        callingUid, parentWindow);
                        //并且token不为TOAST类型则添加失败
                if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
                    Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果是Qs Dialog类型窗口
            } else if (type == TYPE_QS_DIALOG) {
            	//但是token类型不是Qs Dialog则添加失败
                if (token.windowType != TYPE_QS_DIALOG) {
                    Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果AppWindowToken不为空
            } else if (token.asAppWindowToken() != null) {
                Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
                //系统类型窗口但是AppWindowToken不为空
                //将token置为空
                attrs.token = null;
                //重新创建一个新的token
                token = new WindowToken(this, client.asBinder(), type, false, displayContent,
                        session.mCanAddInternalSystemWindow);
            }
            //创建WindowState对象,每一个成功添加的窗口都会
            //在WMS对应一个WindowState,WindowState封装了窗口的信息
            //WindowState里面有个很重要的步骤是调整窗口的Z轴排列顺序
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            if (win.mDeathRecipient == null) {
                // Client has apparently died, so there is no reason to
                // continue.
                Slog.w(TAG_WM, "Adding window client " + client.asBinder()
                        + " that is dead, aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            }

            if (win.getDisplayContent() == null) {
                Slog.w(TAG_WM, "Adding window to Display that has been removed.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
			//会根据一些特殊窗口设置Flag,如截屏窗口设置不能获取事件,
			//状态栏窗口在锁屏被隐藏时需要移除壁纸和键盘保护程序等
            mPolicy.adjustWindowParamsLw(win.mAttrs);
            //设置是否可以显示在当前uid之外的屏幕
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
			//检查特殊窗口如StatusBar,NavigationBar的
			//android.Manifest.permission.STATUS_BAR_SERVICE权限
            res = mPolicy.prepareAddWindowLw(win, attrs);
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }
            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
            	//创建InputChannel,InputChannel用来给WMS和InputDispatcher通信
            	//InputChannel创建同时创建了一对socket,InputDispatch监听
            	//一个socket一端来获取事件处理结果,应用程序监听一个socket一端
            	//来接收InputDispatch发送的事件
                win.openInputChannel(outInputChannel);
            }

           //对TOAST继续进行检查
            if (type == TYPE_TOAST) {
                if (!getDefaultDisplayContentLocked().canAddToastWindowForUid(callingUid)) {
                    Slog.w(TAG_WM, "Adding more than one toast window for UID at a time.");
                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                }
                if (addToastWindowRequiresToken
                        || (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
                        || mCurrentFocus == null
                        || mCurrentFocus.mOwnerUid != callingUid) {
                    mH.sendMessageDelayed(
                            mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
                            win.mAttrs.hideTimeoutMilliseconds);
                }
            }
			//到这里说明窗口是合法的可以添加
            res = WindowManagerGlobal.ADD_OKAY;
            if (mCurrentFocus == null) {
                mWinAddedSinceNullFocus.add(win);
            }

            if (excludeWindowTypeFromTapOutTask(type)) {
                displayContent.mTapExcludedWindows.add(win);
            }

            origId = Binder.clearCallingIdentity();
			//此方法里会创建SurfaceComposerClient建立应用层
			//和SurfaceFlinger的连接
            win.attach();
            //将窗口存入mWindowMap
            mWindowMap.put(client.asBinder(), win);
            if (win.mAppOp != AppOpsManager.OP_NONE) {
                int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
                        win.getOwningPackage());
                if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
                        (startOpResult != AppOpsManager.MODE_DEFAULT)) {
                    win.setAppOpVisibilityLw(false);
                }
            }

            final AppWindowToken aToken = token.asAppWindowToken();
            //如果窗口类型是启动窗口
            if (type == TYPE_APPLICATION_STARTING && aToken != null) {
                //将窗口赋值给token的startwindow
                aToken.startingWindow = win;
                if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
                        + " startingWindow=" + win);
            }

            boolean imMayMove = true;
			//将窗口添加到WindowContainer的WindowList中
            win.mToken.addWindow(win);
            //后面还会处理一些特殊窗口
			......
        return res;
    }

可以看出WMS的addWindow方法逻辑还是很复杂的,主要做了下面这些事:
1.根据窗口的类型检查是否具有相应权限,对非系统窗口和SystemAlert窗口不做检查,其他窗口要检查android.Manifest.permission.SYSTEM_ALERT_WINDOWh和android.Manifest.permission.INTERNAL_SYSTEM_WINDOW权限

2.对窗口的token和type合法性进行检查,比如type是输入法类型,token也必须是输入法类型,要求完全一致,比如当前窗口的父窗口也是子窗口是不允许的等

3.为每一个窗口创建WindowState,在WindowState构造方法中计算窗口的Z轴值

4.给一些特殊窗口设置flag,如截屏不需要获取焦点,StatusBar在没有锁屏时需要移除壁纸和键盘保护等

5.创建InputChannel建立InputDispatcher和应用程序的连接,使得应用程序能够接受InputDispatcher发送的事件以及InputDispatch能够得知应用程序事件处理结果

6.调用win.attach方法建立应用窗口和SurfaceFlinger的连接,使得窗口添加之后能通过SurfaceFlinger合成最终显示到屏幕上

7.根据Z轴确定最终的Window列表

从一个Dialog的创建看Android的窗口机制(上篇)和这篇文章大致梳理了一个窗口的创建与添加,系统所有的窗口流程都类似,希望这两篇文章能够达到窥一斑而知全豹的作用

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android创建一个 dialog,通常需要以下几个步骤: 1. 创建一个 AlertDialog.Builder 对象: ```java AlertDialog.Builder builder = new AlertDialog.Builder(context); ``` 其中 context 是当前 Activity 或 Fragment 的上下文。 2. 设置 dialog 的标题、消息、按钮等属性: ```java builder.setTitle("Dialog Title") .setMessage("Dialog Message") .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 点击确定按钮后的操作 } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 点击取消按钮后的操作 } }); ``` 3. 创建 AlertDialog 对象并显示: ```java AlertDialog dialog = builder.create(); dialog.show(); ``` 完整代码示例: ```java AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("Dialog Title") .setMessage("Dialog Message") .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 点击确定按钮后的操作 } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 点击取消按钮后的操作 } }); AlertDialog dialog = builder.create(); dialog.show(); ``` 需要注意的是,这只是一个简单的创建 dialog 的示例,实际使用中可能需要根据实际需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值