Android 窗口实现原理

一、基本概念

1、窗口显示架构图

多窗口的核心原理其实就是分栈和设置栈边界

在这里插入图片描述

2、Android的窗口分类

Android应用程序窗口,这个是最常见的(拥有自己的WindowToken)譬如:Activity与Dialog
Android应用程序子窗口(必须依附到其他非子窗口才能存在,通常这个被依附的窗口类型Activity窗口) 例如:PopupWindow
Android系统窗口,其中我们最最常见的就是Toast窗口了

3、StackId

【Id:0】Home Stack,这个是Launcher所在的Stack。 其实还有一些系统界面也运行在这个Stack上,例如近期任务
【Id:1】FullScren Stack,全屏的Activity所在的Stack。 但其实在分屏模式下,Id为1的Stack只占了半个屏幕。
【Id:2】Freeform模式的Activity所在Stack
【Id:3】Docked Stack 下文中我们将看到,在分屏模式下,屏幕有一半运行了一个固定的应用,这个就是这里的Docked Stack
【Id:4】Pinned Stack 这个是画中画Activity所在的Stack

4、窗口管理中涉及的几个重要概念

IWindow: APP端窗口暴露给WMS的抽象实例,同时也是WMS向APP端发送消息的Binder通道,它在APP端的实现为W
IWindowSession:WMS服务用于提供给ViewRootImpl来和其进行跨Binder通信的接口
WindowState:WMS端窗口的令牌,与IWindow窗口一一对应,是WMS管理窗口的重要依据
WindowToken:是窗口的令牌,也是窗口分组的依据,在WMS端,和分组对应的数据结构是WindowToken
Token:是在AMS构建Activity对应的ActivityRecord时里面的IApplicationToken的实例,会在Activity创建过程中传递到AMS中,并且Token会在Activity从创建到显示的过程中会在App进程和AMS,WMS之间进行传递
ActivityManagerService
Activity管理机制的Binder服务端,属于一个系统服务。用于管理Activity的各种行为,控制Activity的生命周期,派发消息事件,低内存管理等等。实现了IBinder接口,可以用于进程间通信
ActivityStarter
用来负责处理Activity的Intent和Flags, 还有关联相关的Stack和TaskRecord
ActivityManagerProxy
AMS服务代理端,第三方应用借助该类实现对AMS的远程RPC请求
ActivityRecord
顾名思义,该数据结构和我们的Activiyt相对应,存储者Activiyt的相关信息,并且每个ActivityRecord会对应到一个TaskRecord,ActivityRecord中类型为TaskRecord的成员task,记录所属的Task,这里有一点需要注意的是Activity和ActivityRecord并不是一对一的,而是一对多,因为一个Actitiy可能存在多个启动方式进而导致存在多个ActivityRecord
TaskRecord
一个TaskRecord由一个或者多个ActivityRecord组成,这就是我们常说的任务栈,具有后进先出的特点
ActivityStack
用来管理TaskRecord,它有一个ArrayList类型的成员mTaskHistory,用于存储TaskRecord,系统总是显示位于栈顶的Activity
ActivityDisplay
ActivityDisplay表示一个屏幕,Android支持三种屏幕:主屏幕,外接屏幕(HDMI等),虚拟屏幕(投屏)。一般情况下,即只有主屏幕时,ActivityStackSupervisor与ActivityDisplay都是系统唯一;
ActivityDisplay是ActivityStackSupervisor的内部类,它相当于一个工具类,封装了移除和添加ActivityStack的方法
ActivityStackSupervisor
负责所有Activity栈的管理。内部管理了mHomeStack、mFocusedStack和mLastFocusedStack三个Activity栈。
    mHomeStack管理的是Launcher相关的Activity栈
    mFocusedStack管理的是当前显示在前台Activity的Activity栈
    mLastFocusedStack管理的是上一次显示在前台Activity的Activity栈

5、AMS和WMS交互图

在这里插入图片描述

6、AMS与WMS任务栈对应关系

AMS和WMS在应用窗口这块是有对应关系:
ActivityDisplay----------DisplayContent
ActivityStack------------TaskStack
TaskRecord-------------Task
ActivityRecord----------AppWindowToken

AMS这边依次为:
ActivityDisplay->ActivityStack->TaskRecord->ActivityRecord
WMS依次分为:
DisplayContent(TaskStackContainer)->TaskStack->Task->AppWindowToken

在这里插入图片描述

二、窗口添加并计算

1、Activity窗口尺寸

ViewRootImpl.setView()开始窗口视图的添加
    /**
         这里我们分析的是Activity的DecorView窗口视图添加的逻辑,所以此时不存在父视图的概念,
         不会走到这里,此时的panelParentView为null
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
   
        synchronized (this) {
   
            if (mView == null) {
   
                //顶层DecorView
                mView = view;
                      .......
                       //添加窗口到WMS,mWindow(Binder类型W,传给WMS以便WMS可以调用应用进程方法)
                      res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
                    //mTmpFrame为WMS计算得到的窗口尺寸
                    setFrame(mTmpFrame);
                      ......
                }
           }
    }


通过Seesion.java最终调到WindowManagerService.addWindow

public int addWindow(Session session, IWindow client, int seq,
        WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
        Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) {
   
    int res = mPolicy.checkAddPermission(attrs, appOp);
    //...
    synchronized(mWindowMap) {
   
        //...
        final DisplayContent displayContent = getDisplayContentLocked(displayId);
        if (displayContent == null) {
   
            Slog.w(TAG, "Attempted to add window to a display that does not exist: "
                    + displayId + ".  Aborting.");
            return WindowManagerGlobal.ADD_INVALID_DISPLAY;
        }
        //...
        WindowToken token = mTokenMap.get(attrs.token);
        //...
        // 新的WindowState对象在其构造函数中根据窗口类型初始化了其主序mBaseLayer和mSubLayer
        win = new WindowState(this, session, client, token,
                attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
     ......
    return res;
}

WindowState.WindowState构造初始化

 WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
           WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
           int viewVisibility, final DisplayContent displayContent) {
   
        ...
        //如果是子窗口,使用它依附的窗口类型来计算
        if ((mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW)) {
   
            mBaseLayer = mPolicy.windowTypeToLayerLw(
                    attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
                    + WindowManagerService.TYPE_LAYER_OFFSET;//计算mBaseLayer,关键点1
            //表示子窗口和父窗口的相对位置
            mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);//计算mSubLayer,关键点2
            ...
        } else {
   //非子窗口,直接使用窗口类型来计算
            //注:TYPE_LAYER_MULTIPLIER的值是10000,TYPE_LAYER_OFFSET的值是1000
            mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
                    * WindowManagerService.TYPE_LAYER_MULTIPLIER
                    + WindowManagerService.TYPE_LAYER_OFFSET;
            mSubLayer = 0;//无效,仅在子窗口中有用
            ...
        }
        ...
    }

WindowManagerPolicy.windowTypeToLayerLw

计算主窗口位置
 //根据类型返回窗口的种类,从1-31,
    public int windowTypeToLayerLw(int type) {
   
        if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
   
            return 2;
        }
        switch (type) {
   
        case TYPE_UNIVERSE_BACKGROUND:
            return 1;
        case TYPE_PRIVATE_PRESENTATION:
            return 2;
        case TYPE_WALLPAPER:
            // wallpaper is at the bottom, though the window manager may move it.
            return 2;
        case TYPE_PHONE:
            return 3;
        case TYPE_SEARCH_BAR:
            return 4;
        ...
        case TYPE_HIDDEN_NAV_CONSUMER:
            return 30;
        /// M:JB migration
        case TYPE_TOP_MOST:
            return 31;
        }
        Log.e(TAG, "Unknown window type: " + type);
        return 2;
    }

WindowManagerPolicy.subWindowTypeToLayerLw

计算子窗口位置

public int subWindowTypeToLayerLw(int type) {
   
        switch (type) {
   
        case TYPE_APPLICATION_PANEL:
        case TYPE_APPLICATION_ATTACHED_DIALOG:
            return APPLICATION_PANEL_SUBLAYER;//等于1
        case TYPE_APPLICATION_MEDIA:
            return APPLICATION_MEDIA_SUBLAYER;//等于-2
        case TYPE_APPLICATION_MEDIA_OVERLAY:
            return APPLICATION_MEDIA_OVERLAY_SUBLAYER;//等于-1
        case TYPE_APPLICATION_SUB_PANEL:
            return APPLICATION_SUB_PANEL_SUBLAYER;//等于2
        }
        Log.e(TAG, "Unknown sub-window type: " + type);
        return 0;
    }

WindowManagerService.getLayoutHintLw: 计算窗口的大小

public int addWindow(Session session, IWindow client, int seq,
        WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
        Rect outContentInsets,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凉亭下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值