WMS:窗口的添加过程

2016-12-05  本文已影响112人  81bad73e9053

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

如果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

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;
}
上一篇下一篇

猜你喜欢

热点阅读