美文网首页
WMS:窗口的添加过程

WMS:窗口的添加过程

作者: 81bad73e9053 | 来源:发表于2016-12-05 21:13 被阅读112次

    1.系统窗口的添加过程

    1.1例子

    private void addStatusBarWindow() {
        //获取状态栏的高度
        final int height = getStatusBarHeight(); 
        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,//宽度
                height,
                WindowManager.LayoutParams.TYPE_STATUS_BAR,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT);
        //硬件加速
        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        //gravity
        lp.gravity = getStatusBarGravity();
        lp.setTitle("StatusBar");
        lp.packageName = mContext.getPackageName(); 
        //这个函数会通过inflate资源文件R.layout.super_status_bar来产生一个View并设置属性
        makeStatusBarView();
        //将StatusBarView加入到WMS中
        mWindowManager.addView(mStatusBarWindow, lp);
    }
    
    

    1.2 WMS的获取

    
    registerService(WINDOW_SERVICE, new ServiceFetcher() {
            Display mDefaultDisplay;
            public Object getService(ContextImpl ctx) {
                Display display = ctx.mDisplay;
                if (display == null) {
                    if (mDefaultDisplay == null) {
                        DisplayManager dm = (DisplayManager)ctx.getOuterContext().
                                getSystemService(Context.DISPLAY_SERVICE);
                        mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
                    }
                    display = mDefaultDisplay;
                }
                return new WindowManagerImpl(display);
            }});
    
    

    1.3继承关系

    WindowManagerImpl继承自WindowManager,WindowManager继承自ViewManager

    public interface ViewManager
    {
    
        public void addView(View view, ViewGroup.LayoutParams params);
        public void updateViewLayout(View view, ViewGroup.LayoutParams params);
        public void removeView(View view);
    }
    

    1.4 流程

    WindowManagerImpl的addView###

    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
    

    WindowManagerGlobal的addView###

    WindowManagerGlobal是一个全局且单粒的对象

       public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
            //参数判断
            if (view == null) {
                throw new IllegalArgumentException("view must not be null");
            }
            if (display == null) {
                throw new IllegalArgumentException("display must not be null");
            }
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }
    
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
            if (parentWindow != null) {
                parentWindow.adjustLayoutParamsForSubWindow(wparams);
            }
    
            ViewRootImpl root;//WindowManagerImpl和ViewRoot在同一进程中
            View panelParentView = null;
    
            synchronized (mLock) {
        
                //以前是否添加过这个View对象,findViewLocked从mViews查找是否有和view一样的即可
                int index = findViewLocked(view, false);
                if (index >= 0) {//已经添加过,禁止重复操作
                    if (mDyingViews.contains(view)) { 
                        mRoots.get(index).doDie();
                    } else {
                        throw new IllegalStateException("View " + view
                                + " has already been added to the window manager.");
                    } 
                }
    
       
                //NOTE:根据这个view生成一个ViewRootImpl对象
                root = new ViewRootImpl(view.getContext(), display); 
                view.setLayoutParams(wparams); 
                
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
            } 
            try {
                //真正的跨进程操作开始
                //这个场景中view对应PhoneStatusBar,params对应lp,panelParentView为空
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // 异常处理
                synchronized (mLock) {
                    final int index = findViewLocked(view, false);
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                }
                throw e;
            }
        }
    
    
    

    ViewRootImpl的setView###

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;//一个ViewRoot对应一个ViewTree 
                requestLayout();//发起layout请求 
                try { 
                    //调用Session接口
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mInputChannel);
                } catch (RemoteException e) { 
                    
                }  
            }
        }
    } 
    

    ViewRootImpl作为ViewTree的管理者,同时担负着与WMS进行IPC通信的责任(通过IWindowSession来进行)

    ViewRootImpl的构造函数中IWindowSession的调用流程###

    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();  
        //...... 
    } 
    
    
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            imm.getClient(), imm.getInputContext());
                    float animatorScale = windowManager.getAnimationScale(2);
                    ValueAnimator.setDurationScale(animatorScale);
                } catch (RemoteException e) {
                     
                }
            }
            return sWindowSession;
        }
    } 
    
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
            }
            return sWindowManagerService;
        }
    }
    
    @Override
    public IWindowSession openSession(IInputMethodClient client,
            IInputContext inputContext) { 
        Session session = new Session(this, client, inputContext);
        return session;
    }
    
    

    Session的addToDisplay方法###

    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets,
            InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outInputChannel);
    }
    
    //Session的继承关系
    final class Session extends IWindowSession.Stub
    

    addWindow方法###

    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, InputChannel outInputChannel) {
        int[] appOp = new int[1];
        //权限检查
        int res = mPolicy.checkAddPermission(attrs, appOp);
      
        WindowState attachedWindow = null;
        WindowState win = null;//WindowState用于记录一个Window
        long origId;
        final int type = attrs.type;//窗口类型
    
        synchronized(mWindowMap) {
            //避免重复添加:根据IBinder查询WindowState是否已经存在
            if (mWindowMap.containsKey(client.asBinder())) { 
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
            //类型的值在子窗口的开始值到子窗口的结束值之间,也就是属于子窗口的话
            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                //寻找父窗口
                attachedWindow = windowForClientLocked(null, attrs.token, false); 
            }
            
            if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
                Slog.w(TAG, "Attempted to add private presentation window to a non-private display.  Aborting.");
                return WindowManagerGlobal.ADD_PERMISSION_DENIED;
            }
    
            boolean addToken = false;
            WindowToken token = mTokenMap.get(attrs.token);
            //根据不同窗口类型检查有效性
            if (token == null) {  
            } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                
            } else if (type == TYPE_INPUT_METHOD) { 
            } else if (type == TYPE_WALLPAPER) { 
            } else if (type == TYPE_DREAM) { 
            }
            //WindowState
            win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            if (win.mDeathRecipient == null) {
                 //客户端已经死亡,不需要往下执行
                return WindowManagerGlobal.ADD_APP_EXITING;
            }
            //调整Window属性
            mPolicy.adjustWindowParamsLw(win.mAttrs);
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
    
            res = mPolicy.prepareAddWindowLw(win, attrs);
            
            res = WindowManagerGlobal.ADD_OKAY;
    
            origId = Binder.clearCallingIdentity();
    
            if (addToken) {//前面的步骤新增了token
                mTokenMap.put(attrs.token, token);//把token添加到mTokenMap中
            }
            win.attach();
            //<IBinder,WindowState>
            mWindowMap.put(client.asBinder(), win);
     
            boolean imMayMove = true;
            //把所有window按顺序排列
            if (type == TYPE_INPUT_METHOD) { 
            } else if (type == TYPE_INPUT_METHOD_DIALOG) { 
            } else { 
                //新窗口按顺序添加到WindowList中
                addWindowToListInOrderLocked(win, true);
            }
    
            win.mWinAnimator.mEnterAnimationPending = true;
    
            if (displayContent.isDefaultDisplay) {
                //ContentInset计算
                mPolicy.getContentInsetHintLw(attrs, outContentInsets);
            } else {
                outContentInsets.setEmpty();
            }
    
            //分配层值
            assignLayersLocked(displayContent.getWindowList());
       
        } 
        return res;
    } 
    

    1.首先进行权限检查,如果是非系统窗口直接返回ADD_OKAY;如果添加的是系统窗口需要进一步细化,比如TYPE_TOAST虽然是系统窗口,但应用程序有权限创建;而TYPE_PHONE,TYPE_SYSTEM_ERROR等窗口类型就需要相应的权限许可才能使用(permission.SYSTEM_ALERT_WINDOW)
    2.mWindowMap的类型是HashMap<IBinder,WindowState>,前面的IBinder代表IWindow,所有mWindowMap是WMS中与此IWindow相对应的WindowState的映射。一个IWindow只允许添加唯一的窗口,否则函数直接报错返回,当然一个应用进程中可以持有多个ViewRoot对象,也就意味着它所管理的ViewTree理论上并没有数量上的限制。
    3.如果要添加的窗口类型是子窗口,还要先找它的父窗口,而且需要注意父窗口本身不能是其他窗口的子窗口,否则添加失败。具体的实现是windowForClientLocked

    • token类型是WindowToken

    • attrs.token类型是IBinder,它代表了这个窗口的主人,AMS为每个Activity都分别创建了一个ActivityRecord对象,其本质是一个IBinder,在启动Actiity的时候,这个token会被自动赋值

    • addToken用于指示后续操作中有没有新的Token被添加
      TU 10-8

    如果attrs.token在mTokenMap中找不到对应的WindowToken,说明它在AMS中没有记录此时分为两种情况
    情况一:如果是以下几种窗口类型,则必须在AMS中有案可查,也就是说这些类型的窗口必须实现已经在mTokenMap中添加了对应关系,这是由AMS来完成的,如果此时找不到,说明某些步骤出现了问题,所以程序报错返回

    [FIRST_APPLICATION_WINDOW,LAST_APPLICATION_WINDOW]
    TYPE_INPUT_METHOD
    TYPE_WALLPAPER
    TYPE_DREAM
    

    情况二:除上述的窗口类型外,允许在AMS中没有备案,接着程序会主动为整个窗口生成一个WindowToken,并且置addToken为true

    • 如果在mTokenMap可以找到对应的WindowToken(只有情况一的那几种窗口类型才有可能在mTokenMap中事先添加了对应关系),需要做进一步检查,比如在ApplicationWindow的情况下token.appWindowToken不能为空。除此之外,ViewRoot所指示的windowType必须和AMS中当时登记的类型token.windowType完全一致,否则会出现ADD_BAD_APP_TOKEN错误

    • 如果一切顺利,WMS会为这个窗口添加一个WindowState;this表示WMS,sessino是WMS提供给窗口使用者的IWindowSession,client则是IWindow,即窗口使用者提供给WMS的访问通道,token是WindowToken,attachedWindow代表父窗口(如果存在的话)

    • 如果客户端已经死亡,就不需要往下执行

    • 调用Policy进行窗口参数调整,此时Policy会对某些情况进行约束

    • 如果前面几步中新增了Token对象,那么此时需要将它加入到全局HashMap中,即mTokenMap

    • 因为我们新增一个Window,很有可能影响之前已经排列好的WindowList,所以需要将此新窗口按顺序添加到WindowList中,另外如果用户指定了FLAG_SHOW_WALLPAPER或者本身就是壁纸类型的窗口,还要调用ajustWallpaerWindowsLocked进行壁纸调整

    • getContentInsetHintLw用于计算窗口的ContentInset区域

    • assignLayersLocked负责给上一步中已经排好序的WindowList中的窗口一一分配最终的层级值

    addWindowToListInOrderLocked方法###

    private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
         
        if (win.mAttachedWindow == null) {//没有父窗口的话
            final WindowToken token = win.mToken;
            int tokenWindowsPos = 0;
            if (token.appWindowToken != null) {
                tokenWindowsPos = addAppWindowToListLocked(win);
            } else {//appWindowToken为空的情况,
                addFreeWindowToListLocked(win);
            }
            if (addToToken) {
                if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
                token.windows.add(tokenWindowsPos, win);
            }
        } else {
            addAttachedWindowToListLocked(win, addToToken);
        }
    
        if (win.mAppToken != null && addToToken) {
            win.mAppToken.allAppWindows.add(win);
        }
    }
    

    addFreeWindowToListLocked方法###

    private void addFreeWindowToListLocked(final WindowState win) {
        final WindowList windows = win.getWindowList(); 
        final int myLayer = win.mBaseLayer;
        int i;
        //找到合适的位置
        for (i = windows.size() - 1; i >= 0; i--) {
            if (windows.get(i).mBaseLayer <= myLayer) {
                break;
            }
        }
        i++;
        //添加到WindowList中 
        windows.add(i, win);
        //mWindowsChanged置为true表示windowlist发生改变
        mWindowsChanged = true;
    } 
    

    总体流程就是为整个窗口创建一个与之对应的WindowState,然后加入到WMS的WindowList中

    2.Activity窗口的添加过程

    WMS并不会区分窗口的使用者是谁,只是在层级和权限的处理上会有所不同;当用户启动一个Activity的时候,AMS首先判断该Activity所属的进程是否已经在运行,如果是就向这个进程发送启动指定Activity的命令,否则就先创建该应用进程,运行ActivityThread主线程,然后处理启动Activity的操作。当AMS发现一个Activity即将启动时,会把相关信息告知WMS。

    final void startActivityLocked(ActivityRecord r, boolean newTask,
            boolean doResume, boolean keepCurTransition, Bundle options) {
    
        TaskRecord task = null;
        if (!newTask) {
            
            boolean startIt = true;
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                task = mTaskHistory.get(taskNdx);
                if (task == r.task) { 
                    if (!startIt) { 
                        //addAppToken  把ActivityRecord的相关信息传递过去
                        mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                                r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                                (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
                                r.userId); 
                        return;
                    }
                    break;
                } else if (task.numFullscreen > 0) {
                    startIt = false;
                }
            }
        }
        //....
    }    
    
    
    
    @Override
    public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
            int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId) { 
        synchronized(mWindowMap) {
            AppWindowToken atoken = findAppWindowToken(token.asBinder());
            if (atoken != null) { 
                return;
            }
            //这里记录的都是ActivityRecord的信息
            atoken = new AppWindowToken(this, token);
            atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
            atoken.groupId = taskId;
            atoken.appFullscreen = fullscreen;
            atoken.showWhenLocked = showWhenLocked;
            atoken.requestedOrientation = requestedOrientation; 
    
            Task task = mTaskIdToTask.get(taskId);
            if (task == null) {
                task = createTask(taskId, stackId, userId, atoken);
            } else {
                task.addAppToken(addPos, atoken);
            }
            //加入<IBinder,WindowToken>
            mTokenMap.put(token.asBinder(), atoken); 
            atoken.hidden = true;
            atoken.hiddenRequested = true; 
        }
    }
       
    

    把ActivityRecord记录到<IBinder,WindowToken>,如果没有这一步,activity调用addWindow会失败###

    handleResumeActivity

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        //这句话导致onResume调用
        r = performResumeActivity(token, clearHide, reason);
    
        if (r != null) {
            final Activity a = r.activity;
    
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                //DecorView是Activity的整棵ViewTree的最外围
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);//可见性
                //获取WindowManger
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();//获取属性
                a.mDecor = decor;
                //窗口类型
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
     
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);//将窗口添加到WMS中
                }
                //......
    } 
    

    也是调用addWindow来处理,WMS会为他们生成WindowState,区别在于添加到WindowList###

        private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
            if (DEBUG_FOCUS_LIGHT) Slog.d(TAG, "addWindowToListInOrderLocked: win=" + win +
                    " Callers=" + Debug.getCallers(4));
            if (win.mAttachedWindow == null) {
                final WindowToken token = win.mToken;
                int tokenWindowsPos = 0;
                if (token.appWindowToken != null) {//appWindowToken的情况
                    tokenWindowsPos = addAppWindowToListLocked(win);
                } else {
                    addFreeWindowToListLocked(win);
                }
                if (addToToken) {
                   
                    token.windows.add(tokenWindowsPos, win);
                }
            } else {
                addAttachedWindowToListLocked(win, addToToken);
            }
    
            if (win.mAppToken != null && addToToken) {
                win.mAppToken.allAppWindows.add(win);
            }
        }
    

    addAppWindowToListLocked方法###

    
    private int addAppWindowToListLocked(final WindowState win) {
        //WindowState包含了IWindow信息和WindowToken信息
        final IWindow client = win.mClient;
        final WindowToken token = win.mToken; 
        //系统当前所有窗口
        final WindowList windows = win.getWindowList();
        final int N = windows.size();//当前所有窗口数量
        WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
        int tokenWindowsPos = 0;
        int windowListPos = tokenWindowList.size();
        if (!tokenWindowList.isEmpty()) { //该应用程序已经包含有一个窗口
          
            if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
                // Base windows go behind everything else.
                WindowState lowestWindow = tokenWindowList.get(0);
                placeWindowBefore(lowestWindow, win);
                tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
            } else { 
                //...............
            }
            return tokenWindowsPos;//位置
        }
     
        WindowState pos = null; 
        //该应用程序没有窗口的时候找到合适的位置插入 
        windows.add(i, win);
        mWindowsChanged = true;
        return tokenWindowsPos;
    }
    

    相关文章

      网友评论

          本文标题:WMS:窗口的添加过程

          本文链接:https://www.haomeiwen.com/subject/jydlmttx.html