Android 11源码分析:从Activity的setContent方法看渲染流程(3)再看Window

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

前言

在第一篇文章,知道了一个App的视图层级,并且分析了我们写的XML是如何添加到content中的,知道了顶级视图DecorView和PhoneWindow的关系。

在第二篇文章里,介绍了Window的addView,removeView,upDateViewLayout三个重要方法。知道了View是由ViewRootImpl控制绘制,Window是由WMA控制显示到窗口中。

这篇文章,我将再次窥探一下Window。以下常量定义在接口WindowManager中

不同类型的Window

Window有三个分类。

Window 层级
应用Window 1-99
子Window 1000~1999
系统Window 2000-2999

想来这个分级大家也经常看到吧。我们知道Activity用的就是应用应用Window,然后我们自己写的弹窗,是子Window,子Winodw必须依赖在一个应用Window上才可以显示出来,要不然怎么叫Window呢,比如我们的的弹窗,其次是系统Window,这个一般是系统级别的使用,比如我们的toast,我们当然也可以使用啦。

下面我在代码里论证一下,这个1-99 ,1000-1999是咋定义的。

android.view.WindowManager

@WindowType
public int type;

应用Window
/**
 * Start of window types that represent normal application windows.
 */
public static final int FIRST_APPLICATION_WINDOW = 1;
 */
public static final int TYPE_APPLICATION        = 2;
public static final int TYPE_APPLICATION_STARTING = 3;
public static final int TYPE_DRAWN_APPLICATION = 4;

/**
 * End of types of application windows.
 */
public static final int LAST_APPLICATION_WINDOW = 99;
下面是子Window
/**
 * Start of types of sub-windows.  The {@link #token} of these windows
 * must be set to the window they are attached to.  These types of
 * windows are kept next to their attached window in Z-order, and their
 * coordinate space is relative to their attached window.
 */
public static final int FIRST_SUB_WINDOW = 1000;
......
/**
 * End of types of sub-windows.
 */
public static final int LAST_SUB_WINDOW = 1999;
下面的是系统Window
/**
 * Start of system-specific window types.  These are not normally
 * created by applications.
 */
public static final int FIRST_SYSTEM_WINDOW     = 2000;
/**
 * End of types of system windows.
 */
public static final int LAST_SYSTEM_WINDOW      = 2999;
......

复制代码

我们看到对于应用Window就定义了5个,可没有从1定义到99。当然我只要设置的值在1-99之间,这个Window就是应用Window类型了。

不知道大家有没有写过类似悬浮球的应用,然后可以也可以跟随手指移动更新位置,最早见到这种我是在苹果手机上看到的。一开始以为很难,其实就是通过上一篇提到的upDateViewLayout传入坐标系参数就可以完成了,非常简单。当然如果想要定义成在切换到其他App的时候,悬浮球还存在,我们就需要申请悬浮窗权限,并且将悬浮球Window设置为TYPE_SYSTEM_ALERT。

视图分析

我们在一个简单的Demo上,弹起一个弹窗,看看视图层级会是哪样的。

先看一张手机截图的

1ed5165532cbb405ecf7595e994cfc9.jpg
再看一下AS 视图分析的

1629442805(1).png
在Activity的界面里,我弹起一个弹窗,同时也弹出一个toast,然后打开了个第三方的悬浮窗时间的APP。在视图分析里,因为AS的分析工具只能分析当前进程,所以只我们看到了右边有2个DecorView,也就是2个PhoneWindow。

在同一个屏幕里,是会出现多个Window的,而管理这些Window的,就是WindowManagerService

8cb63d0e676d1c4e09f8231c6d17318.png

再看WindowManagerService,WindowManagerGlobal,ViewRootImpl

WindowManagerGlobal

之前说到attach内的wm.addView。方法最后执行处是在WindowManagerGlobal,内部还有几个变量。

android.view.WindowManagerGlobal

负责和WMS通信
private static IWindowSession sWindowSession;
当前应用内所有View
private final ArrayList<View> mViews = new ArrayList<View>();
当前应用内所有ViewViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
当前应用内所有View对应的参数
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();
复制代码

sWindowSession。

WindowManagerGlobal本身就是单例对象,sWindowSession又被static修饰。毫无意义,一个application就只有一个sWindowSession对象。

通过上一篇的分析,ViewRootImpl会通过sWindowSession去通知WMS对屏幕相关的显示管理。

但是这个sWindowSession到底是什么类型呢?

  @UnsupportedAppUsage
    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
                    InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                    IWindowManager windowManager = getWindowManagerService();
                    关键代码
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
复制代码

我们知道windowManager其实就是WindowManageService。

com.android.server.wm.WindowManageService

  @Override
    public IWindowSession openSession(IWindowSessionCallback callback) {
        return new Session(this, callback);
    }

复制代码

sWindowSessionq原来是Session类。

mViews,mRoots,mParams

上一篇对mViews的描述是存储了所有Window所对应的View。这是我《Android开发艺术探索》看到的描述,第一次看到这句话的时候,我可能会对这句话有一些过多的思考。

  1. mViews存储的是所有Window所对应的DecorView
  2. mViews存储的是所有Window所对应的所有View

这其实细想还是有区别的。比如说我简单的Demo里,一个Actvity里有一个TextView,一个ImgView。 对于第一种理解,那mViews的大小是1,而对于第二种理解,那mViews就不只是1了,有TextView,有ImgView,还有id为content的FrameLayout等等等等。

mViews有两个操作,添加和删除。

android.view.WindowManagerGlobal

       添加
       public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
            ......
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            ......
            }
       删除
        void doRemoveView(ViewRootImpl root) {
        boolean allViewsRemoved;
        synchronized (mLock) {
            final int index = mRoots.indexOf(root);
            if (index >= 0) {
                mRoots.remove(index);
                mParams.remove(index);
                final View view = mViews.remove(index);
                mDyingViews.remove(view);
            }
            allViewsRemoved = mRoots.isEmpty();
        }
    }
            
复制代码

这两个方法在上一篇分析过,只不过重点不在mViews,mRoots,mParams,现在再来看一看就明白了。mViews,mRoots,mParams这三兄弟的添加和操作永远保持一致,也就是说数组的长度都是一样的,一一对应的。比如mViews第二个DecorView,他的布局参数存在mParams的第二个元素里,他的ViewRoot也在mRoots的第二个元素里存着。

那也就是说WindowManagerGlobal只对Window的DecorView做管理,内部嵌套了多少层,是内部自己的事,他不关心,也关心不过来。 mViews存储的就是所有Window所对应的DecorView。DecorView又对应着一个Window,所以WindowManagerGlobal管理着Window

mDyingViews

mDyingViews这个就好理解了,立马就是一些将要被删除DecorView

简单了解ViewRootImpl

在setView,方法开始,他会requestLayout刷新布局

android.view.ViewRootImpl

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    
    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

复制代码

checkThread方法里,我们看到了熟悉的报错:不能在子线程更新UI

scheduleTraversals之前也提供是View绘制的入口,内部会触发 View 绘制流程:measure –> layout –> draw。然后内部通过WindowSession与WMS通信,完成界面的渲染流程。

关于ViewRootImpl具体的解析不在当前系列中,后面对View系统的分析再做详解。

简单了解WindowManagerService

不知道大家有没有一种感觉,虽然各种很多地方好像看起来操作的是Window,但是最后实际操作的对象还是View,或者是Window下的DecorView。因为Window是一个抽象的存在,实际上显示的是他的DecorView。所以WMS是管理当前状态下,哪个View在最上层显示,所以他其实是也是管理View,并非Window,但是View又在于Window中。所以会给人一种WMAS其实管理的是Window。他管理的其实是属于哪个Window下的DecorView罢了

com.android.server.wm.WindowManagerService

 final ArraySet<Session> mSessions = new ArraySet<>();

复制代码

WindowManagerService内部有一个Sessions列表,联系上之前ViewRootImpl与WindowManagerService通信用到的sWindowSessionq似乎都联系上了。

1629528023(1).png

WMS内部维护了个Session的集合,与对应的Application进行通信

至于WMS是如何控制屏幕显示哪个View的就不说了,也不说啥不能陷入代码的森林。主要原因还是我也只迷迷糊糊的知道个大概。等猴年马月我自己吃透了,我再出来献丑。

WindowManagerService(WMS)和ActivityManagerService(AMS)是Android中FrameWork即为重要的一环。

总结

  1. 初始化Activity的时候,在其attach方法会创建一个PhoneWindow,与Activity绑定,并且将PhoneWindow和WindowManger,这里有2个绑定操作。
  2. 在setContent的时候,会为PhoneWindow创建默认的DecorView,经过一些系列方法调用后,选择一个与Theme匹配的布局给DecorView,同时把我们写XML放到其内部一个id为Content的FrameLayout中

以上是视图层级初始化梳理,下面是视图渲染的梳理.由handleResumeActivity方法下wm.addView开始

  1. addView最后是由WindowManagerGlobal触发
  2. WindowManagerGlobal是个单例,内部有个当前Application与WMS通信的Session静态变量,内部还有存储当前Application下所有Window对应DecorView的集合有mViews,,当然还mRoots,mParams
  3. ViewRooyImpal负责View的绘制同时通过Session与WMS进行通信
  4. WMS和AMS一样是系统服务,WMS主要负责窗口的管理,当然还有我文中没有提到的事件分发。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享