WindowManagerService详述

一、WindowManagerService是什么?

WindowManagerService,简称wms服务,见名知意首先是管理window窗口的,它在管理当前系统中所有的window窗口,由此可以引申出另一个重要作用,它主管分配设备产生的touch事件、keydown事件,因为wms能知道当前系统中哪个window最适合来处理某个事件(它在管理所有的window,可以遍历来挑选最优window),硬件产生的某个事件最终是要被软件应用所消费的,那些view、button的按键事件就是通过window再传递给View体系来响应按键的。

1.1、wms作用:

  1. 管理窗口,viewRootImpl.setView–wms.addWindow–交给surfaceFlinger
  2. 分发从InputManagerService传递来的事件(用socket跨进程),分发给某个进程

1.2、wms启动方式

与ams、ims类似都是由SystemServer进程启动,在startOtherServices函数中调用wms的main函数,SystemServer很熟悉就不用展示源码了

1、SystemServer在源码位置:/* framework/base/service/java/com/android/server/SystemServer.java*/
2、ams源码位置:/* framework/base/service/core/java/com/android/server/am*/
3、wms源码位置:/* framework/base/service/core/java/com/android/server/wm*/

1.3、wms基础功能

wms以AIDL的方式来构建binder服务架构,在源码未编译前只有IWindowManager.aidl文件,其提供的所有功能都在此文件中,下面罗列出几个接口函数:

  1. openSession----与wms建立session连接,类似surfaceFlinger里的client
  2. addWindowToken
  3. prepareAPPTransition----窗口动画
  4. setAPPStartingWindow----启动窗口
  5. screenShotApplications----屏幕截图

1.4、wms与应用程序的跨通信

wms运行在SystemServer进程,应用程序访问wms还是用binder驱动来实现IPC通信,wms本身是实名binder服务,可以通过serviceManager获取,然后通过openSession函数建立session连接(由wms提供的匿名服务,IWindowSession),反过来wms如果需要和某个应用程序通信(window),是根据应用进程传递的匿名binder服务,也就是说应用(window)自己提供一个匿名的binder server(IWinder)供wms反向调用。
先看打开session源码例子:

/*framework/base/core/java/com/android/view/ViewRootImpl.java*/
    public ViewRootImpl(Context context, Display display) {
        this(context, display, WindowManagerGlobal.getWindowSession(),
                false /* useSfChoreographer */);
    }

/*framework/base/core/java/com/android/view/WindowManagerGlobal.java*/
public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    // Emulate the legacy behavior.  The global instance of InputMethodManager
                    // was instantiated here.
                    // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
                    InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub());
  ...

而IWinder则是:static class W extends IWindow.Stub (源码在/framwork/base/core/java/com/android/view/ViewRootImpl.java)

1.5、wms内部组织方式

当一个activity被启动时,首先要在ams中注册,amsnew出来ActivityRecord来记录这个Activity;另外Activity是专门用来显示UI,每个activity对应一个window窗口,所以wms也需要记录联动–以WindowState来保存一个窗口相关的信息。wms还用AppWindowToken来对应ams中的ActivityRecord。

二、window的窗口属性

虽然Android系统中的窗口非常多,不过从用途来考虑,我们可以将其分为三大类:

  1. Application Window
  2. System Window
  3. Sub Window

细分的若干子类型都能从WindowManager.java中找到,接下来用表格详细展示window窗口的属性

2.1、Application Window

普通应用程序的窗口都属于这一类

Type Description
FIRST_APPLICATION_WINDOW=1 应用程序窗口类型的起始值
TYPE_BASE_APPLICATION=1 应用程序窗口类型的基础柱,其他窗口类型一次为基础
TYPE_APPLICATION=2 普通应用程序窗口类型
TYPE_APPLICATION_STARTING=3 应用程序的启动窗口类型,应用程序本身不可使用,Android系统自动为应用程序启动前展示的窗口,当应用程序启动后就会消失
LAST_APPLICATION_WINDOW=99 应用程序窗口类型的最大值

2.2、Sub Window

见名知意,子窗口,附着在某些窗口中,包括下列一些子类型

Type Description
FIRST_SUB_WINDOW=1000 子窗口类型的起始值
TYPE_BAPPLICATION_PANEL=FIRST_SUB_WINDOW 应用程序的panel子窗口,在它的父窗口之上显示
TYPE_APPLICATION_MEDIA=FIRST_SUB_WINDOW+1 用于显示多媒体内容的子窗口,位于父窗口之下
TYPE_APPLICATION_SUB_PANEL=FIRST_SUB_WINDOW+2 也是panel子窗口,位于父窗口以及所有TYPE_APPLICATION_PANEL子窗口之上
TYPE_APPLICATION_ATTACHED_DIALOG=FIRST_SUB_WINDOW+3 Dialog子窗口,类似menu类型
TYPE_APPLICATION_MEDIA_OVERLAY=FIRST_SUB_WINDOW+4 多媒体窗口的覆盖层,位于TYPE_APPLICATION_MEDIA和应用程序窗口之间,通常透明才有意义,未开放
TYPE_SUB_WINDOW=1999 子窗口类型的结束值

2.3、System Window

Type Description
FIRST_SYSTEM_WINDOW=2000 系统窗口类型的起始值
TYPE_STATUS_BAR=FIRST_SYSTEM_WINDOW 系统状态栏窗口
TYPE_SEARCH_BAR=FIRST_SYSTEM_WINDOW+1 搜索条窗口
TYPE_PHONE=FIRST_SYSTEM_WINDOW+2 通话窗口,一般来电通话位于系统状态栏之下,其他应用窗口之上
TYPE_SYSTEM_ALERT=FIRST_SYSTEM_WINDOW+3 Alert窗口,如电量不足的警告窗口,一般位于所有其他应用程序之上
TYPE_KEYGUAED=FIRST_SYSTEM_WINDOW+4 屏保窗口
TYPE_TOAST=FIRST_SYSTEM_WINDOW+5 短暂提示框窗口
TYPE_SYSTEM_OVERLAY=FIRST_SYSTEM_WINDOW+6 系统覆盖层窗口,不可接受input焦点否则会与屏保冲突
TYPE_PRIORITY_PHONE=FIRST_SYSTEM_WINDOW+7 电话优先窗口,屏保状态下显示来电窗口
TYPE_SYSTEM_DIALOG=FIRST_SYSTEM_WINDOW+8 RecentAppsDialog就是这种类型窗口
TYPE_KEYGUARD_DIALOG=FIRST_SYSTEM_WINDOW+9 屏保时显示的对话框
TYPE_SYSTEM_ERROR=FIRST_SYSTEM_WINDOW+10 系统错误窗口
TYPE_INPUT_METHOD=FIRST_SYSTEM_WINDOW+11 输入法窗口
TYPE_INPUT_METHOD_DIALOG=FIRST_SYSTEM_WINDOW+12 输入法窗口之上的对话框窗口
TYPE_WALLPAPER=FIRST_SYSTEM_WINDOW+13 壁纸窗口
TYPE_STATUS_BAR_PANEL=FIRST_SYSTEM_WINDOW+14 滑动状态栏出现的窗口
TYPE_SECURE_SYSTEM_OVERLAY=FIRST_SYSTEM_WINDOW+15 / TYPE_DRAG=FIRST_SYSTEM_WINDOW+16 / TYPE_STATUS_BAR_SUB_PANEL=FIRST_SYSTEM_WINDOW+17 / TYPE_POINTER=FIRST_SYSTEM_WINDOW+18 暂未开放
TYPE_NAVIGAGTION_BAR=FIRST_SYSTEM_WINDOW+19 导航条
TYPE_VOLUME_OVERLAY=FIRST_SYSTEM_WINDOW+20 系统音量条
TYPE_BOOT_PROGRESS=FIRST_SYSTEM_WINDOW+21 启动时的进度条窗口
TYPE_HIDDEN_NAV_CONSUMER=FIRST_SYSTEM_WINDOW+22 导航条隐藏时用于消费触摸事件的伪窗口
TYPE_DREAM=FIRST_SYSTEM_WINDOW+23 / TYPE_NAVIGATION_BAR_PANEL=FIRST_SYSTEM_WINDOW+24 暂未开放
TYPE_UNIVERSE_BACKGROUND=FIRST_SYSTEM_WINDOW+25 多用户系统中,将显示给所有用户
TYPE_DISPLAY_OVERLAY=FIRST_SYSTEM_WINDOW+26 用于模拟第二个显示设备
TYPE_MAGNIFICATION_OVERLAY=FIRST_SYSTEM_WINDOW+27 类似放大镜的效果,用于放大显示某部分内容
TYPE_RECENTS_OVERLAY=FIRST_SYSTEM_WINDOW+28 与TYPE_SYSTEM_DIALOG基本类似,区别在于TYPE_RECENTS_OVERLAY只显示在一个用户的屏幕上
LAST_SYSTEM_WINDOW=2999 系统窗口结束

每种窗口类型都有一个数值,三类窗口间隔是:

  1. Application Window:1-99
  2. Sub Window:1000-1999
  3. System Window:2000-2999

当某个应用进程向wms申请窗口时,需要指定所需的窗口类型(当然应用程序基本上不允许创建系统窗口,会在权限检查时被拒绝),然后wms根据用户申请的窗口类型以及当前系统中已有窗口的情况给这个窗口最终分配一个"层级值",系统在运行期间,很可能会出现同一种窗口类型当前有很多个窗口,比如当前有三个应用程序在执行,那类型为TYPE_APPLICATION的应用程序窗口就会有3个,因此wms需要根据具体情况调整他们的最终层级值。数值越大的窗口,在wms中优先级就越高,最终在屏幕显示时就越靠近用户

2.4、wms关于层级值的分配规则

相同类型的window,要加上窗口间的层级间隔 WINDOW_LAYER+MULTIPLIER = 5,wms的层级值layers是按照WindowType来转换的,有个映射转换表,源码则是在(framework/base/services/core/java/com/android/server/wm/WindowState.java位置的WindowState构造函数中)

/*framework/base/services/core/java/com/android/server/wm/WindowState.java*/
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
            mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);

注:WindowType就是2.1、2.2、2.3里的各种窗口Type。
接下来看WindowType到Layers的映射转换表

WindowType Layer value
[FIRST_APPLICATION_WINDOW,LAST_APPLICATION_WINDOW] LAST_APPLICATION_WINDOW 2
TYPE_WALLPAPER WALLPAPER_LAYER 2
TYPE_PHONE PHONE_LAYER 3
TYPE_SEARCH_BAR TYPE_SEARCH_LAYER 4
TYPE_SYSTEM_DIALOG SYSTEM_DIALOG_LAYER 5
TYPE_TOAST TOAST_LAYER 6
TYPE_PRIORITY_PHONE PRIORITY_PHONE_LAYER 7
TYPE_SYSTEM_ALERT SYSTEM_ALERT_LAYER 8
TYPE_INPUT_METHOD INPUT_METHOD_LAYER 9
TYPE_INPUT_METHOD_DIALOG INPUT_METHOD_DIALOG_LAYER 10
TYPE_KEYGUARD KEYGUARD_LAYER 11
TYPE_KEYGUARD_DIALOG KEYGUARD_DIALOG_LAYER 12
TYPE_DREAM SCREENSAVER_LAYER 13
TYPE_STATUS_BAR_SUB_PANEL STATUS_BAR_SUB_PANEL_LAYER 14
TYPE_STATUS_BAR STATUS_BAR_LAYER 15
TYPE_STATUS_BAR_PANEL STATUS_BAR_PANEL_LAYER 16
TYPE_VOLUME_OVERLAY VOLUME_OVERLAY_LAYER 17
TYPE_SYSTEM_OVERLAY SYSTEM_OVERLAY_LAYER 18
TYPE_NAVIGATION_BAR NAVIGATION_BAR_LAYER 19
TYPE_NAVIGATION_BAR_PANEL NAVIGATION_BAR_PANEL_LAYER 20
TYPE_SYSTEM_ERROR SYSTEM_ERROR_LAYER 21
TYPE_DRAG DRAG_LAYER 22
TYPE_SECURE_SYSTEM_OVERLAY SECURE_SYSTEM_OVERLAY_LAYER 23
TYPE_BOOT_PROGRESS BOOT_PROGRESS_LAYER 24
TYPE_POINTER POINTER_LAYER 25
TYPE_HIDDEN_NAVCONSUMER HIDDEN_NAVCONSUMER_LAYER 26
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
            mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);

对于SubWindow来说,窗口类型取决于父窗口类型,

  1. mPolicy.getWindowLayerLw(parentWindow)获取到的映射值需要*TYPE_LAYER_MULTIPLIER (10000),这是因为系统运行的时候很有可能出现大量相同WindowType的窗口,所以得有一个足够大的间隔以供分配,
  2. 追加 TYPE_LAYER_OFFSET(1000)随后加的这个偏移值,源码注释是为了设计同一层级一整组窗口设。
  3. 对于子窗口类型来说,还要特意计算mSubLayer,子窗口与父窗口息息相关,所以mSubLayer其实是用于决定这个子窗口应该偏移父窗口的数值大小。例如:TYPE_APPLICATION_PANEL和TYPE_APPLICATION_ATTACHED_DIALOG类型的子窗口对于的mSubLayer都是APPLICATION_PANEL_SUBLAYER=1;而TYPE_APPLICATION_MEDIA类型的子窗口对于的mSubLayer是APPLICATION_MEDIA_SUBLAYER=-2.子窗口在显示时有可能在父窗口之上,也有可能再其下

2.5、窗口策略

    上一节我们说到了mPolicy.getWindowLayerLw(parentWindow),这个mPolicy是什么?WindowManagerPolicy窗口策略,我们知道Android不是单一的产品设备系统,手机、平板、现在都要进军电脑了,那UI窗口的样式必定要有差异,例如手机设备一般是有StatusBar,平板pad电脑一般就没有,它是CombinedBar,这种差异化的窗口属性组成了某种设备的UI属性,Google试图在一套源码上实现各式各样的差异,动态的配置就注定需要一个manager来管理,这就是WindowManagerPolicy的设计理念。

    在Android源码实现中,与window policy相关的类主要是4个,WindowManagerPolicy,PhoneWindowManager,PolicyManager和Policy。
PolicyManager:
1、makeNewWindow–new一个window,默认是PhoneWindow
2、makeNewWindowManagerPolicy–产生一个管理Window的Policy,
WindowManagerPolicy是窗口管理策略的接口类,

2.6、窗口属性(LayoutParams)

除了WindowType,wms中还有很多其他的窗口属性,在APP开发时我们会遇到大量的View控件,后者天然存在各种LayoutParams,各种属性都存在于WindowManager:

2.6.1 Type,就是窗口类型

2.6.2 Flags,窗口标志,默认值0

Flags Description
FLAG_ ALLOW_LOCK_WHILE_SCREEN_ON 当此窗口可见,即使屏幕开启时也允许锁屏
FLAG_DIM_BEHIND 在窗口后面的所有东西都将变暗dimmed
FLAG_NOT_FOCUSABLE / FLAG_NOT_TOUCHABLE / FLAG_NOT_TOUCHABLE_MODAL 窗口不获取焦点和事件,事件会直接传递给窗口后的下一级,一般前两者同时设置
FLAG_TOUCHABLE_WHEN_WAKING 当设备进入睡眠状态,设置此标志可以使此窗口获得第一次的触摸事件,因为此时用户不知道他们当下点击的到底是哪一个窗口,因此都是由系统来指定
FLAG_KEEP_SCREEN_ON 只要这个窗口可见,屏幕就一直亮不会锁屏
FLAG_LAYOUT_IN_SCREEN 窗口显示时不考虑系统窗口,例如Status Bar
FLAG_LAYOUT_NO_LIMITS 允许窗口超过屏幕区域
FLAG_FULLSCREEN 隐藏所有屏幕装饰窗口,例如Status Bar,在视频全屏播放中很常用
FLAG_FORCE_NOT_FULLSCREEN 正好和FLAG_FULLSCREEN 相反
FLAG_SECURE 窗口内容被认为是保密的,不允许截屏
FLAG_SCALED 按照用户提供参数进行伸缩调整
FLAG_IGNORE_CHEEK_PRESSES 有时候用户和屏幕会贴的很近,例如打电话的时候,这种情况下出现的某些事件不应该去响应的
FLAG_LAYOUT_INSET_DECOR 只能和FLAG_LAYOUT_IN_SCREEN 同时使用,当设置全屏显示布局时,应用窗口部分内容可能还会被系统窗口覆盖,设置此Flag后,系统就会计算系统装饰窗口占的区域,避免这种问题
FLAG_SHOW_WHEN_LOCKED 使窗口能在锁屏窗口之上显示
FLAG_WALLPAPER 让壁纸在这个窗口之后显示,例如Launcher APP,当窗口是透明、半透明时就可以看到后面的壁纸背景
FLAG_TURN_SCREEN_ON 窗口显示时将屏幕点亮
FLAG_DISMISS_KEYGUARD 只要不是secure lock,设置这个Flag可以解锁屏幕,
FLAG_HARDWARE_ACCELERATED 指明窗口是否需要硬件加速

可以通过以下方法来设置窗口属性

    Window w = activity.getWindow();
    w.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | 
                WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON )

2.6.3 systemUiVisibility

名称就能看出来此变量表示的是系统UI的可见性,但SystemUIVisibility的可选Flags值定义在View里,而不是WindowManager

Flags Description
SYSTEM_UI_FLAG_VISIBLE View视图请求显示System UI
SYSTEM_UI_FLAG_LOw_PROFILE View视图请求进入 low profile 模式,代表Status Bar/Navigation icons有可能被调暗,一般游戏、电子书阅读器等应用可能用得上
SYSTEM_UI_FLAG_HIDE_NAVIGATION 用于隐藏Navigation导航栏的隐藏,这个标志经常和FLAG_LAYOUT_IN_SCREEN和FLAG_LAYOUT_IN_SCREEN一起使用,这样才能真正的全屏显示
SYSTEM_UI_FLAG_FULLSCREEN 整个View将进入全屏显示,它与WindowManager.LayoutParams.FLAY_FULLSCREEN视觉效果基本相同,不过和Window提供的标志不同的是,对于使用Window.FEATURE_ACTION_BAR_OVERLAY的ActionBar,SystemUi是这个标志也能把它隐藏掉。一般来说如果是暂时的全屏显示需求,可以用SYSTEM_UI_FLAG_FULLSCREEN,而如果是长时间的全屏显示比如游戏界面中,就去用Window的标志
SYSTEM_UI_FLAG_LAYOUT_STABLE 系统将尽力保证UI布局稳定

因为这个Flags是作用在view上的,因此当我们希望Activity中整个View都使用同一个systemUIVisibility,可以给DecorView设置,其实我们看上述的那些Flag,基本是影响了整个窗口,如果只给某个Button设置也不像话。下面看使用示例:

    int newViewFlags = View.SYSTEM_UI_FLAG_LOw_PROFILE|SYSTEM_UI_FLAG_HIDE_NAVIGATION|SYSTEM_UI_FLAG_FULLSCREEN;
    getWindow().getDecorView().setSystemUiVisibility(newViewFlags);

三、应用或者系统的窗口是怎么添加倒wms中的?

Android中展示的窗口虽然很多,从使用角度分类,其实能把它分成应用程序使用的窗口和系统使用的窗口,分别叫成“应用窗口”、“系统窗口”,我们分别追踪两者添加过程。

3.1系统窗口的添加过程

使用SystemUI应用中StatusBar举例说明:

/*/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java*/
private void addStatusBarWindow() {
        makeStatusBarView();
        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
    }

/*frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java*/
public void add(View statusBarView, int barHeight) {
        mLp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                barHeight,
                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
                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                PixelFormat.TRANSLUCENT);
        mLp.token = new Binder();
        mLp.gravity = Gravity.TOP;
        mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
        mLp.setTitle("StatusBar");
        mLp.packageName = mContext.getPackageName();
        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        mStatusBarView = statusBarView;
        mBarHeight = barHeight;
        mWindowManager.addView(mStatusBarView, mLp);//Step1
        mLpChanged = new WindowManager.LayoutParams();
        mLpChanged.copyFrom(mLp);
    }

上面就是StatusBar能够显示在屏幕上最关键的部分,mWindowManager.addView(),其作用是加入到wms中,变量mWindowManager是一个WindowManager对象

/*frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java*/
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

WindowManager是一个接口类,继承ViewManager,WindowManager的实现类是WindowManagerImpl,伪代码:mWindowManager = new WindowManagerImpl;于是下一步转向WindowManagerImpl.addView();

/*framework/base/core/view/WindowManagerImpl.java*/
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

于是又转向WindowManagerGlobal.addView;

/*framework/base/core/view/WindowManagerGlobal.java*/
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
          ...
          ViewRootImpl root;
          root = new ViewRootImpl(view.getContext(), display);
          mViews.add(view);
          mRoots.add(root);
          mParams.add(wparams);
          root.setView(view, wparams, panelParentView);//Step1
          ...
    }

继续转向ViewRootImpl.setView,

/*framework/base/core/view/ViewRootImpl.java*/
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        ...
        //Step1,设置响应Vsync垂直同步刷新,就是由硬件产生通过
        //SurfaceFlinger传输的vsync_app信号
        requestLayout();
        /*mWindowSession,应该要很熟悉了,开篇讲wms所提供的接口中
        第一个接口就是openSession()
        */
        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
        ...
    }
   //mWindowSession是从哪里来的??
   mWindowSession = WindowManagerGlobal.getWindowSession();
   /*framework/base/core/view/WindowManagerGlobal.java*/
   public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                    IWindowManager windowManager = getWindowManagerService();
                    //这里我们看到了openSession
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

mWindowSession.addToDisplay()就是应用窗口与wms的通信了,类型是IWindowSession ,第一个参数mWindow,IWindow类型的匿名Binder服务,用来让wms主动和当前窗口通信用的,我们能发现没有给wms传递View树相关的参数,这是因为wms不关系你这个窗口里具体展示的是哪些view,wms只管这个窗口的size大小、窗口层级值等等(都放在WindowManager.LayoutParams属性中),接下来经过Binder驱动的跨进程,流程就从应用进程到了wms的进程中,IWindowSession在wms服务端的实现是Session,函数addToDisplay(早期版本这个函数叫add,能感觉出来Android在为多显示屏不停的做努力)又调用到了WMS的addWindow:

/*framework/base/service/core/java/com/android/server/wm/Session.java*/
 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);
    }  

/*framework/base/service/core/java/com/android/server/wm/WindowManagerService.java*/
    public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
            int requestUserId) {
        int[] appOp = new int[1];
        int res = mPolicy.checkAddPermission(attrs, appOp);/*Step1.权限检查*/
        final int type = attrs.type;//窗口类型
        synchronized (mGlobalLock) {
            ...
            if (mWindowMap.containsKey(client.asBinder())) {/*Step2.避免重复添加*/
                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);
                ...
            }
            //Step4
            token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
            } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
                atoken = token.asAppWindowToken();
                if (atoken == null) {
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (atoken.removed) {
                    return WindowManagerGlobal.ADD_APP_EXITING;
                } else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {
                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                } ...
                 else if (token.asAppWindowToken() != null) {
                attrs.token = null;
                token = new WindowToken(this, client.asBinder(), type, false, displayContent,
                        session.mCanAddInternalSystemWindow);
            }
        }
        ...
        /*Step5.*/
        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
        if (win.mDeathRecipient == null) {//Step6.当客户端死亡就不执行
            return WindowManagerGlobal.ADD_APP_EXITING;
        }
        ...
        final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
        displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
                    Binder.getCallingUid());/*Step7.调整window的属性*/
        win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
        res = displayPolicy.prepareAddWindowLw(win, attrs);
        ...
        if (displayPolicy.getLayoutHintLw/*Step8、ContentInset计算*/(win.mAttrs, taskBounds, displayFrames, floatingStack,
                    outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
                res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
        }
        ...
        win.getParent().assignChildLayers();//Step9、分配最终的层级值
        ...  
    }       

Step1、进行权限检查,typeLAST_SYSTEM_WINDOW,如果窗口类型不是系统窗口,直接返回ADD_OKAY,非系统窗口是任何条件下都能够添加的;反之说明是系统窗口,就需要进一步来细化判断。
Step2、变量mWindowMap是一个< IBinder,WindowState>类型的map,IBinder是IWindow,因此mWindowMap是wms与IWindow相对应的WindowState的映射。下面还有< IBinder,WindowToken>类型的mTokenMap,两者是有区别的,一个IWindow只允许添加唯一的窗口(在wms看来,就是WindowState,而对于应用程序来说是可以持有多个ViewRoot,其ViewTree没有理论的上限)
Step3、如果要添加的窗口是子窗口,就得找出它的父窗口,要注意这个父窗口不能同时是其他窗口的子窗口,不然会添加失败
Step4:

  1. token类型:WindowToken
  2. attrs.token类型:IBinder,代表这个窗口的所有者-主人,AMS为每一个Activity分别创建一个ActivityRecord对象,本质就是一个IBinder,在启动Activity时,token就会自动被赋值
  3. 源码中列举的几种WindowType必须在AMS中能查到,else的时候就允许ams找不到对应的Token,而且会new WindowToken

Step5、一切顺利后,wms就会为这个窗口创建WindowState,WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], seq, attrs, viewVisibility, session.mUid,session.mCanAddInternalSystemWindow);
    WindowState构造函数中的几个参数:this是WMS自身;session是wms提供给窗口使用的IWindowSession;client是IWindow,窗口提供给wms的调用通道;token是WindowToken;parentWindow是父窗口;。。。

Step6、当客户端死亡就不执行 例如应用进程crash
这样一个System window就add完毕

3.2 Activity窗口的添加过程

    其实对于wms来说,不论你是应用窗口也好,系统窗口也好,它的整体流程基本一样,只不过层级和权限有差异。当启动一个activity,ams首先判断前者所属进程是否在运行,有的话就发启动命令给这个进程,否则先让zygote来fork应用进程,创建ActivityThread主线程,然后在H这个handler中处理启动activity的操作。
    activity天生拥有显示UI界面的能力,且需要wms服务参与支持,当ams发现一个activity需要启动时,它需要将对应信息告知wms,那么activity进程到底是什么时候调用addView,进而由wms处理addWindow呢?

/*framework/base/core/java/android/app/ActivityThread.java*/
    public void handleResumeActivity(handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason){
        //activity的onResume函数将被调用
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        ...
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();//Step1
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        l.softInputMode |= forwardBit;
        ViewRootImpl impl = decor.getViewRootImpl();
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                wm.addView(decor, l);//Step2
    }

handlerResumeActivity这个函数里的逻辑我们应该要感觉很熟悉:
Step1、与系统窗口添加的逻辑相同,ViewManager是接口类,最终实现类是WindowManagerImpl,Step2、其addView和系统窗口添加时的addView完全相同,会走到wms服务端的Session.java中,再去调用wms.addWindow,这些流程完全一样,接下来我们看看应用窗口和系统窗口的差异究竟在哪?

3.2.1 WMS添加应用窗口和系统窗口的差异

根据上一个小节我们知道wms在添加应用窗口或者系统窗口的主体框架上完全相同,实质上wms就是等某个用户使用openSession建立一个session通道,然后用这个session调用addToDisplay经过Binder驱动把窗口的参数传递到wms,就这么简单,一般wms都会为用户生成一个WindowState来管理窗口,然而,后续将用于窗口添加到全局窗口列表中的处理流程就会产生差异,主要是应用窗口的层级值一般会比系统窗口低,比如StatusBar在默认的时候它一直会显示出来而且会overlay应用窗口的一部分,也就是前面说的权限和层级值不同,一般系统窗口会更重要一些,详细逻辑可以翻源码去查看

四、应用层的Surface

我们能看到wms只关心window的size、attr和layer,SurfaceFlinger才是把众多window数据合成起来的核心系统服务,显然当wms将window的size或者Z-order等layer属性调整后必须要通知SurfaceFlinger,回想SurfaceFlinger体系(SurfaceFlinger传送门),有两种本地窗口,一种是用于在本地设备适配OpenGL ES渲染后的窗口-FrameBufferNativeWindow(提供给Gralloc这个hal使用);一种是用于封装SurfaceFlinger管理的BufferQueue-Surface(提供给应用层使用),而UI界面的绘制必须要有画板,应用的view才能在其上作画,因此无论是系统窗口还是应用窗口,都必须向SurfaceFlinger申请layer拿到FrameBuffer才能进行。

SurfaceFlinger服务端(BufferQueue)生产的就是surface,只不过它属于native层(c++),这些知识点有了才能整体将Android显示系统串联起来,此小节的作用就是阐述应用层是怎么拿到BufferQueue提供的本地surface

此时应当要有个疑惑点 :Surface和Window的区别是什么?先预留

4.1、应用层Surface申请流程

应用层Surface是从哪里开始申请init?还是从ViewRootImpl开始,当Gralloc模块的场频信号产生sync_app通过SurfaceFlinger上报到ViewRootImpl的Choreographer,mTraversalRunnable回调执行,doTraversal->performTraversals->relayoutWindow,从relayoutWindow能看出来,当目前的FrameBuffer不符合(或者首次调用performTraversals),它需要重新申请buffer,详细来看relayoutWindow函数:

/*framework/base/core/view/ViewRootImpl.java*/
    public final Surface mSurface = new Surface();//Step1
    private final SurfaceControl mSurfaceControl = new SurfaceControl();
    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
        int relayoutResult = mWindowSession.relayout(mWindow, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                mTempControls, mSurfaceSize);//Step2
         ...
         mSurface.copyFrom(mSurfaceControl);//Step3
         ...
}

    mWindowSession根据上小节应该很熟悉了,与wms通信的session,显然relayout是通过binder调用到wms服务端的函数;mSurfaceControl是封装JNI调用的类,它只是个中介,最终为了赋值给mSurface;而mSurface就是应用层使用的画板。
    这里的逻辑很容易看懂,应用进程通过IwindowSession来调用relayout函数,让wms服务端去跟SurfaceFlinger申请画板,最后copyFrom赋值给应用层的Surface来使用,要注意在wms申请之前应用层的Surface是个空壳子,只有copyFrom赋值后才是有效的FrameBuffer(应用层就是Surface)。

首先思考下wms申请的画板,应用进程要如何来用呢?毕竟Linux或者说Android系统是沙盒进程,还是使用binder来跨进程传递,回想下SurfaceFlinger,后者的服务端GraphicBuffer产生一个buffer,怎么通过binder跨进程传递给client进程,wms就怎么做的,只不过SurfaceFlinger是用c++,wms是java语言,用aidl来做,来复习下aidl语言。relayout函数在wms服务的Session匿名binder服务中

/*framework/base/core/java/android/IWindowSession.aidl*/
    int relayout(IWindow window, in WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility,
            int flags, long frameNumber, out ClientWindowFrames outFrames,
            out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
            out InsetsState insetsState, out InsetsSourceControl[] activeControls,
            out Point outSurfaceSize);
/*frameworks/base/service/core/java/com/android/server/wm/Session.java*/
    public int relayout(IWindow window, WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
            ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
      int res = mService.relayoutWindow(this, window, attrs,
                requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
                outActiveControls, outSurfaceSize);//mService应该很熟悉,wms  
    }

其实这里主要想说下aidl语法中的 in out,看到SurfaceControl 形参了没,前面带个out,就代表它是一个函数出参,aidl工具在生成java文件的时候,会为客户端的proxy和服务端的stub适配参数的parcable,详细来说就是服务端的stub会用writeToParcel写,而客户端的proxy会用readFormParcel读,binder跨进程中的对象就是这么传递的(或者说这样把一个对象的内容还原出来的)

4.2、WMS如何管理Surface?

继续看relayoutWindow逻辑(看wms中relayoutWindow函数逻辑),可以联想activity跳转逻辑,一个Activity显示,必然伴随着上一个Activity destroy,可想而知在这个过程中Surface必然也会存在创建与销毁,也就是viewVisibility和Gone,我们先来分析Visibility时申请Surface的情况

/*framework/base/service/core/java/com/android/server/wm/WindowManagerService.java*/
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags,
            long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
    synchronized (mGlobalLock) {
        //Step1、寻找对应窗口的WindowState
        final WindowState win = windowForClientLocked(session, client, false);
        ...
        // We should only relayout if the view is visible, it is a starting window, or the
        // associated appToken is not hidden.
        //用很大篇幅来判断和配置surface
        final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                        || win.mActivityRecord.isClientVisible());
        //真正开始创建surface
        if (shouldRelayout) {
                result = win.relayoutVisibleWindow(result, attrChanges);
                try {
                    result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                } catch (Exception e) {
                    return 0;
                }
        if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
            winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
        } else {
            try {
                    outSurfaceControl.release();///生成Surface失败
                } finally {
                        ...
               }
       }
       if (focusMayChange) {
           if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {//调整分配层级
                    imMayMove = false;
           }
       }
       // updateFocusedWindowLocked() already assigned layers so we only need to
       //reassign them at this point if the IM window state gets shuffled
       boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
       if (toBeDisplayed) {//重新调整
            // Little hack here -- we -should- be able to rely on the function to return
            // true if the IME has moved and needs its layer recomputed. However, if the IME
            // was hidden and isn't actually moved in the list, its layer may be out of data
            // so we make sure to recompute it.
            displayContent.assignWindowLayers(false /* setLayoutNeeded */);
        }
    }
}

private int createSurfaceControl(SurfaceControl outSurfaceControl, int result, WindowState win,
            WindowStateAnimator winAnimator) {
        if (!win.mHasSurface) {
            result |= RELAYOUT_RES_SURFACE_CHANGED;
        }

        WindowSurfaceController surfaceController;
        try {
            //wms并不直接生成Surface,而是由WindowStateAnimator提供createSurfaceLocked
            surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        if (surfaceController != null) {
            surfaceController.getSurfaceControl(outSurfaceControl);
        } else {
            outSurfaceControl.release();
        }

        return result;
    }
/*frameworks/base/service/core/java/com/android/server/wm/WindowStateAnimator.java*/
    WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
        final WindowState w = mWin;

        if (mSurfaceController != null) {
            return mSurfaceController;
        }
        mChildrenDetached = false;

        if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
            windowType = SurfaceControl.WINDOW_TYPE_DONT_SCREENSHOT;
        }

        w.setHasSurface(false);
        resetDrawState();
        mService.makeWindowFreezingScreenIfNeededLocked(w);

        int flags = SurfaceControl.HIDDEN;
        final WindowManager.LayoutParams attrs = w.mAttrs;

        if (mService.isSecureLocked(w)) {
            flags |= SurfaceControl.SECURE;
        }
        calculateSurfaceBounds(w, attrs, mTmpSize);
        final int width = mTmpSize.width();
        final int height = mTmpSize.height();
        mLastClipRect.set(0, 0, 0, 0);
        // Set up surface control with initial size.
        try {

            final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0;
            final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
            if (!PixelFormat.formatHasAlpha(attrs.format)
                    // Don't make surface with surfaceInsets opaque as they display a
                    // translucent shadow.
                    && attrs.surfaceInsets.left == 0
                    && attrs.surfaceInsets.top == 0
                    && attrs.surfaceInsets.right == 0
                    && attrs.surfaceInsets.bottom == 0
                    // Don't make surface opaque when resizing to reduce the amount of
                    // artifacts shown in areas the app isn't drawing content to.
                    && !w.isDragResizing()) {
                flags |= SurfaceControl.OPAQUE;
            }

            mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
                    attrs.getTitle().toString(), width, height, format, flags, this,
                    windowType, ownerUid);
            mSurfaceController.setColorSpaceAgnostic((attrs.privateFlags
                    & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
            setOffsetPositionForStackResize(false);
            mSurfaceFormat = format;
            w.setHasSurface(true);
        } catch (OutOfResourcesException e) {
            mService.mRoot.reclaimSomeSurfaceMemory(this, "create", true);
            mDrawState = NO_SURFACE;
            return null;
        } catch (Exception e) {
            mDrawState = NO_SURFACE;
            return null;
        }
        mLastHidden = true;
        return mSurfaceController;
    }

能看到这里是创建Surface(WindowSurfaceController对象是操作surface的controller,找到这个类随意看一眼就能理顺逻辑,源码行数很短),这里我们也没有找到surface和SurfaceFlinger通信的地方,继续来追踪流程

4.2、WMS中的Surface如何与SurfaceFlinger通信

Android显示系统中,与Surface相关的类非常多,应用层、native层:Surface.java、Surface.cpp、Isurface、IGraphicBufferProducer等等,需要针对这些做好区分认知。
对于java层应用来说,Surface.java是其直接使用的绘图画板,但这个Surface是需要底层的Surface赋值,而后者buffer的来源是从SurfaceFlinger体系中的BufferQueue来quest一个buffer。

  1. ViewRootImpl中创建空壳Surface
  2. 当wms调用WindowStateAnimator的createSurfaceLocked时,生成一个真正的Surface,由Surfacecontrol来管理
  3. Surfacecontrol封装的是jni调用的各种native函数
/*frameworks/base/core/jni/android_view_SurfaceControl.cpp*/

```cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
        jobject metadataParcel) {
    ScopedUtfChars name(env, nameStr);
    sp<SurfaceComposerClient> client;//Step1、出现了在SurfaceFlinger体系中的代表SurfaceFlingerComposerClient
    if (sessionObj != NULL) {//Step2、
        client = android_view_SurfaceSession_getClient(env, sessionObj);
    } else {
        client = SurfaceComposerClient::getDefault();
    }
    SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
    sp<SurfaceControl> surface;
    LayerMetadata metadata;
    Parcel* parcel = parcelForJavaObject(env, metadataParcel);
    if (parcel && !parcel->objectsCount()) {
        status_t err = metadata.readFromParcel(parcel);
    }

    status_t err = client->createSurfaceChecked(
            String8(name.c_str()), w, h, format, &surface, flags, parent, std::move(metadata));
    if (err == NAME_NOT_FOUND) {
        return 0;
    } else if (err != NO_ERROR) {
        jniThrowException(env, OutOfResourcesException, NULL);
        return 0;
    }

    surface->incStrong((void *)nativeCreate);
    return reinterpret_cast<jlong>(surface.get());
}

Step1、能看到出现了SurfaceFlinger体系中熟悉的SurfaceFlingerComposerClient,surface在前者的帮助下有序申请和使用buffer,
Step2、当sessionObj 不等于null时能看到直接去获取SurfaceFlingerComposerClient,也就是说在java层就创建好了,通过jni传递下来,它是从哪来的?

来源是这样的:
当addWindow被调用时,wms会为窗口建立WindowState,并将Session保存到其成员变量mSession中,接着WindowState.attach被调用,mSession就通过windowAddedLocked进一步生成mSurfaceSession,WindowState构造时,同时生成内部mWinAnimator,后者构造时也将Session保存在mSession成员中,,其成员变量在createSurfaceLocked中传递给Surface的构造函数,当ViewRootImpl的Surface开始调用nativeReadFromParcel函数后,后者会创建出sp< IGraphicBufferProducer> gbp,然后将gbp用构造参数的形式jni返回给java层的Surface,因此上述java层会有SurfaceFlingerComposerClient。

这样上层的surface就和surfaceflinger所管理的BufferQueue建立了关联,由view->activity->wms->surfaceflinger这条线就能完整串起来了

4.3、窗口大小的计算过程

以activity窗口来说,除了应用窗口至少是有statusBar+输入法窗口,输入法窗口在AndroidManifast.xml中可以指定配置(windowSoftInputMode属性,可叠加)-以state开头的属性描述的是当前activity有焦点的时候,软键盘是隐藏还是显示;以adjust开头的部分属性描述的是如何调整activity窗口用来容纳软键盘,当然也可能有导航栏等等其他窗口,因此窗口大小的计算其实就是计算这些(屏幕大小是固定的,系统需要显示的宽高确定下来以后,再重新计算应用窗口大小),根本上来说窗口只需要w、h两个参数,但现实情况会很复杂,因此在ViewRootImpl中包括了以下相关变量

Member Description
int mWidth Height 当前真实宽高
Rect mWinFrame 当Activity的窗口大小需要改变时,WMS通过W.resized接口通知客户端,mWinFrame就用于记录WMS提供的宽高
Rect mPendingVisibleInsets 同上,用于表示可见区域
Rect mPendingContentInsets 同上,用于表示内容区域
int desiredWindowWidth desiredWindowHeight 中间值,用于表示期望的宽高值,不一定是最终值

contentInsets内容区域,visibleInsets可见区域,一般来说俩者一样大,但有时候输入法窗口会覆盖在窗口之上,此时可见区域就比内容区域小得多了(因为输入法窗口占了一大部分窗口的size)。
performTraversals是整个窗口计算的起点,如果窗口尺寸发生变化则(或者是首次调用)调用relayoutWindow(),进而调用relayout函数,其后者通知wms去计算的binder函数,这些在前边小节已有介绍,WMS服务的relayoutWindow接口函数中对窗口大小的计算流程是:performLayoutAndPlaceSurfacesLocked()-> performLayoutAndPlaceSurfaceLockedLoop()-> performLayoutAndPlaceSurfacesLockedInner()->performLayoutLockedInner(),这个函数有三个重要节点(接口函数都在PhoneWindowManager类中):beginLayoutLw()–初始化、layoutWindowLw()–执行计算过程、finishLayoutLw()–完成清理工作。layoutWindowLw()–执行计算过程中会根据各自系统窗口产生父窗口大小、屏幕大小、内容区域、可见区域,然后由win.computeFrameLw来计算最终结果,高版本上上述函数可能名称上有差异,或者所存在的位置不同,但梳理流程逻辑的本质是一样的。经过这一系列调用,WindowManagerService.relayoutWindow才算是真正结束了,wms将其计算出的结果值通过函数几个出参(outContentInsets、outVisibleInsets、outConfig、outSurface等)返回给ViewRootImpl,进而来影响UI的显示。

4.4、启动窗口的创建与销毁

我们应该或多或少都接触过,新activity启动时,在应用进程尚未真正显示之前,由系统显示一个启动窗口,直到activity主界面显示,启动窗口才会消失,也叫APP的冷启动优化,这个启动窗口完全是由系统来管理其生命周期的,可以猜测必然是AMS和WMS交替完工,

当AMS在处理startActivity时,会走到Task.startActivityLocked,如果发现目标进程未启动或者是一个新的task,此时就会准备启动一个Preview window,受两个变量影响

  • SHOW_APP_STARTING_PREVIEW,bool变量,默认为true
  • doShow,默认为true,如果要启动的activity在一个新的Task中,且flags为FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,

两者同时为true,在Task.startActivityLocked中,ActivityRecord@showStartingWindow()-> ActivityRecord@addStartingWindow(),到wms服务中,下一步由PhoneWindowManager来makeNewWindow,由WindowManagerImpl实现(addView函数),跳到ViewRootImpl@setView,最后通知IWindowSession.add(),这里能看出来添加一个启动窗口和普通应用窗口在这些流程上是没有区别的,这个window(窗口)的属性如下:

  • 窗口大小,宽和高都是match_parent,满屏显示,因为是顶层父窗口
  • 窗口类型,肯定就是type_application_starting
  • 窗口标题,由getText(labelRes,nonLocalizedLabel)计算
  • 窗口标志,临时窗口,因此不应该有touchable或者focusable

启动窗口的销毁就正好和创建反着来,一旦应用程序的主窗口显示,与之相关的启动窗口就会被销毁(系统在某些特殊情况下也会移除窗口),发送消息让WMS来处理(remove_starting或者是finished_starting)->PhoneWindowManager@removeStartingWindow->WindowManagerImpl@removeView->ViewRootImpl@die->doDie->dispatchDetachedFromWindow->Surface@release->Session@remove()。

流程看着复杂,可以分解:

  1. 窗口的拥有者,通过WindowManagerImpl管理它名下的所有window,具体就是mViews、mRoots、mParams三个成员数组,启动窗口基本上和普通应用窗口没有区别的,只是拥有者的身份不同,当WMS处理remove_starting,首先调用PhoneWindowManager@removeStartingWindow,然后由WindowManagerImpl执行removeView,直到调用doDie移除。
  2. Surface,当窗口被移除,与之对应的FrameBuffer自然无需再存在了,因此在dispatchDetachedFromWindow函数中,会使用mSurface.release来释放这个surface
  3. WMS中的WindowState,WindowState是用来在wms中记录窗口状态的,当移除窗口后WindowState当然也需要更新信息,在dispatchDetachedFromWindow函数中使用sWindowSession.remove(mWindow)来调用wms的删除窗口服务(IWindowSession,服务端是由Session.java实现),最后同performLayoutAndPlaceSurfacesLocked来调整当前系统的窗口界面,最终体现到终端屏幕。

wms的服务大体上描述的差不多,后边源码需要看官们亲自去翻,有问题欢迎留言讨论。

你可能感兴趣的:(Android核心系统服务,android)