美文网首页Android开发经验谈Android技术知识Android开发
Android开发技术——车机技术之WMS学习

Android开发技术——车机技术之WMS学习

作者: 谁动了我的代码 | 来源:发表于2023-01-31 15:32 被阅读0次

    / 窗口管理 /

    窗口管理核心类介绍

    窗口管理使用到的 DisplayContent,WindowToken 和 WindowState。

    DisplayContent

    用来管理一个逻辑屏上的所有窗口,有几个屏幕就会有几个 DisplayContent。使用 displayId 来区分。

    处于不同 DisplayContent 的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合。因此,就这几个方面来说,DisplayContent 就像一个孤岛,所有这些操作都可以在其内部独立执行。

    DisplayContent 类声明:

    class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>{    // Mapping from a token IBinder to a WindowToken object on this display.    private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();}   
    
    1. DisplayContent 的子容器是其内部类 DisplayChildWindowContainer
    2. DisplayContent 内部使用:IBinder 为 key,WindowToken 为 value 的键值对保存在 HashMap 中。
    3. DisplayContent 是在 Window 添加到 WMS 的时候初始化的。

    WMS

    public int addWindow(Session session, ..){    final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);    ..}
    

    mRoot 是 RootWindowContainer 类型的对象,看名字就知道其是窗口容器的根。说明在 Window 体系中,RootWindowContainer 节点是容器最顶端的父容器。

    class RootWindowContainer extends WindowContainer<DisplayContent> {    DisplayContent getDisplayContentOrCreate(int displayId) {        DisplayContent dc = getDisplayContent(displayId);        if (dc == null) {            final Display display = mService.mDisplayManager.getDisplay(displayId);            if (display != null) {                              dc = createDisplayContent(display);                     }        }        return dc;    }   DisplayContent getDisplayContent(int displayId) {      for (int i = mChildren.size() - 1; i >= 0; --i) {          final DisplayContent current = mChildren.get(i);           if (current.getDisplayId() == displayId) {              return current;           }      }      return null;  }}
    

    其继承了 WindowContainer,这里的 DisplayContent 是一个泛型声明,表示其子容器的类型是 DisplayContent, 在 getDisplayContent 方法中也可知,其 mChildren 列表是 DisplayContent 的集合。这也变相的说明 DisplayContent 也是一个容器。

    WindowToken

    类声明:

    class WindowToken extends WindowContainer<WindowState>
    

    表明 WindowToken 也是子容器,其子容器是 WindowState,所以 WindowState 也是一个容器。

    WindowToken 在窗口体系中有两个作用:

    1. 应用组件标识:将属于同一个应用组件的窗口组织在一起,这里的应用组件可以是:Activity、InputMethod、Wallpaper 以及 Dream。WMS 在对窗口的管理过程中用 WindowToken 来指代一个应用组件。例如在进行窗口的 Z-Order 排序时,属于同一个 WindowToken 的窗口会被安排在一起。
    2. 令牌作用:WindowToken 由应用组件或其管理者负责向 WMS 声明并持有,应用组件在需要更新窗口时,需要向 WMS 提供令牌表明自己的身份, 并且窗口的类型必须与所持有的 WindowToken 的类型一致。

    但是系统窗口是个例外,并不需要提供 token,WMS 会隐式声明一个WindowToken。那是不是说谁都可以添加系统窗口了呢?非也,在 addWindow 开始处就会调用下面代码:

    mPolicy.checkAddPermission()
    

    它要求客户端必须拥有 SYSTEM_ALERT_WINDOW 或INTERNAL_SYSTEM_WINDOW 权限才能创建系统类型的窗口。Window 和WindowToken 关系如下:

    image

    WindowState

    类声明:

    class WindowState extends WindowContainer<WindowState>
    

    表明 WindowState 也是一个 WindowContainer 容器,但是其子容器也是WindowState,一般窗口有子窗口 SUB_WINDOW 的情况下,WindowState 才有子容器节点。WindowState 在 WMS 中表示一个 Window 窗口状态属性,其内部保存了一个 Window 所有的属性信息。其与 View 以及 WindowToken 关系如下:

    image

    如何查看当前设备 Window 窗口状态命令?

    adb shell dumpsys window windows    
    Window #9 Window{884cb45 u0 com.android.messaging/com.android.messaging.ui.conversationlist.ConversationListActivity}:    mDisplayId=0 stackId=3 mSession=Session{f1b7b8e 4307:u0a10065} mClient=android.os.BinderProxy@a512fbc    mOwnerUid=10065 mShowToOwnerOnly=true package=com.android.messaging appop=NONE    mAttrs={(0,36)(828xwrap) gr=BOTTOM CENTER sim={adjust=pan forwardNavigation} ty=APPLICATION fmt=TRANSLUCENT wanim=0x7f130015      fl=DIM_BEHIND ALT_FOCUSABLE_IM HARDWARE_ACCELERATED      vsysui=LIGHT_STATUS_BAR LIGHT_NAVIGATION_BAR}    Requested w=828 h=290 mLayoutSeq=220    mBaseLayer=21000 mSubLayer=0    mToken=AppWindowToken{3f9efb8 token=Token{2b272cc ActivityRecord{55a41e u0 com.android.messaging/.ui.conversationlist.ConversationListActivity t8}}}    mAppToken=AppWindowToken{3f9efb8 token=Token{2b272cc ActivityRecord{55a41e u0 com.android.messaging/.ui.conversationlist.ConversationListActivity t8}}}...  
    

    下面笔者以窗口的添加操作为例讲解 WMS 的窗口管理。

    窗口的添加操作

    public int addWindow(Session session, IWindow client, int seq,        WindowManager.LayoutParams attrs, int viewVisibility, int displayId,        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,        InputChannel outInputChannel) {    ...    int res = mPolicy.checkAddPermission(attrs, appOp);//1    ...    synchronized(mWindowMap) {        final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);//2        if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {            parentWindow = windowForClientLocked(null, attrs.token, false);//3        }        ...                    WindowToken token = displayContent.getWindowToken(                hasParent ? parentWindow.mAttrs.token : attrs.token);//4        if (token == null) {                                          final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();            token = new WindowToken(this, binder, type, false, displayContent,                    session.mCanAddInternalSystemWindow);//5        }         ...        final WindowState win = new WindowState(this, session, client, token, parentWindow,                appOp[0], seq, attrs, viewVisibility, session.mUid,                session.mCanAddInternalSystemWindow);//6        ...        mPolicy.adjustWindowParamsLw(win.mAttrs);//7        ...        if  (openInputChannels) {            win.openInputChannel(outInputChannel);//8        }        ...        mWindowMap.put(client.asBinder(), win);//9        ...        win.mToken.addWindow(win);//10        ...        displayContent.assignWindowLayers(false /* setLayoutNeeded */);//11        //12        if (focusChanged) {            mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);        }        mInputMonitor.updateInputWindowsLw(false /*force*/);    }    ...    return res;}
    
    • 注释1:检查当前 Window 的 token 等权限合法性。
    • 注释2:这在介绍 DisplayContent 已经说过,使用 RootWindowContainer 的子容器中获取一个 DisplayContent,如果子容器集合中不存在,则去获取一个,并添加到 child 集合中
    • 注释3:如果是 Dialog 等子窗口,则获取父窗口,没有就报找不到父窗口的异常。
    • 注释4:使用 attr.token 去 displayContent 的键值对 mTokenMap 中获取对应的 WindowToken,WindowToken 中保存了一组 Window。
    • 注释5:如果4中 WindowToken 为 null,则创建一个 WindowToken,传入 app 层传入的 attr.token 以及 displayContent 对象,内部会将这个创建的 WindowToken 保存到 displayContent 中
    • 注释6:创建一个 WindowState,并传入所有关于 Window 相关的属性,这样 WindowState 在 WMS 中就是以一个 Window 性质存在了、WindowState 构造过程中会将其添加到 WindowToken 中去。
    • 注释7:根据 mPolicy 调整 window 的 attr 属性,mPolicy 的实现类是PhoneManagerPolicy。
    • 注释8:执行 WindowState 的 openInputChannel,这里主要是打通和 Input 系统的通道,用于接收 IMS 的输入事件请求。
    • 注释9:将客户端 app 层的 Window 对象和 WindowState 关联上,这样 WMS 就可以通过 Window 找到 WMS 中的 WindowState 对象。
    • 注释10:win.mToken 是前面创建的 WindowToken 对象,所以此处就是将WindowState 加入到 WindowToken 的子容器集合中。
    • 注释11:分配窗口的层级,这里的 setLayoutNeeded 参数为 false,说明不需要进行 Layout 操作。

    这里小结下 addWindow 方法,主要就是创建了一个和 Window 一一对应的 WindowState 对象,并将 WindowState 插入到父容器 WindowToken 的子容器集合中,而 WindowToken 又保存在 DisplayContent 的键值对集合中。三种关系可以简单总结如下:

    image

    / 窗口动画 /

    我们知道在 Android 内部有两种动画,Window 切换移动动画以及 app 层的 View 的动画, 动画操作的是 View 而 Window 切换操作的是 Surface,对不同层级的 SurfaceControl 进行操纵,会产生不同的动画效果,注意区分。

    我们这里涉及到的是 Window 切换移动动画。

    但是不管是 View 的动画还是 Window 切换操作,对底层屏幕刷新来说都是针对不同帧动画来说,所以会涉及到 VSync 同步信号相关知识。

    核心类

    WindowStateAnimator

    类声明:

    Keep track of animations and surface operations for a single WindowState.

    用来管理一个 Window 的动画操作的,在 WindowState 构造方法中创建,说明每个 Window 窗口都对应一个 WindowStateAnimator。

    WindowState(WindowManagerService service...){    mWinAnimator = new WindowStateAnimator(this);}
    

    WindowAnimator

       /**       * Singleton class that carries out the animations and Surface operations in a separate task    * on behalf of WindowManagerService.      */       public class WindowAnimator
    

    看方法说明,这个类还是用于 WMS 中的窗口动画以及 Surface 操作的单例工具类,WMS 将动画的工作都委托他来处理。其在 WMS 构造的时候创建了实例。

    WindowAnimator(final WindowManagerService service) {    mService = service;    ..    mWindowPlacerLocked = service.mWindowPlacerLocked;//1 这个类用于Surface的摆放    AnimationThread.getHandler().runWithScissors(            () -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);//2mAnimationFrameCallback = frameTimeNs -> {//3    synchronized (mService.mWindowMap) {        mAnimationFrameCallbackScheduled = false;    }    animate(frameTimeNs);};}
    

    注释1处创建了一个 WindowSurfacePlacer 对象,这个对象是用于 Surface 的摆放的操作,说明 WindowAnimator 还支持 Surface 的各种操作 注释2处使用AnimationThread 线程进行 Window 的动画操作,AnimationThread 内部使用的是 HandlerThread 机制,说明其内部也创建了一个异步消息处理机制。注释3处 mAnimationFrameCallback 类型是 Choreographer.FrameCallback。

    FrameCallback 在这篇文章中有讲过,其就是给 Choreographer 设置一个回调,在 Choreographer 接收到 VSync 信号时,在 doFrame 中触发这个回调,一般是用来监听帧率等操作。

    而这里是在接收到 doFrame 的时候回调的是一个 animate(frameTimeNs) 动画处理的方法。animate 函数执行流程很长,包括更新壁纸、转屏动画等逻辑均包含在其中。那么 mAnimationFrameCallback 回调是什么时候注册到 Choreographer 中去的呢?

    WindowAnimator的scheduleAnimation 方法:

    void scheduleAnimation() {    if (!mAnimationFrameCallbackScheduled) {        mAnimationFrameCallbackScheduled = true;        mChoreographer.postFrameCallback(mAnimationFrameCallback);    }}
    

    在外部需要进行动画的时候,就会优先 scheduleAnimation,将 mAnimationFrameCallback 注册到 Choreographer 中去。我们重点来看 animate 方法,这个方法内部有这么段代码。

    dc.updateWindowsForAnimator(this);
    

    表示为了动画去更新 Windows,可以进入看看。

    void updateWindowsForAnimator(WindowAnimator animator) {    mTmpWindowAnimator = animator;    forAllWindows(mUpdateWindowsForAnimator, true /* traverseTopToBottom */);}boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {...final int count = mChildren.size();for (int i = 0; i < count; i++) {    final DisplayChildWindowContainer child = mChildren.get(i);    if (child == mImeWindowsContainers && mService.mInputMethodTarget != null) {        // In this case the Ime windows will be processed above their target so we skip        // here.        continue;    }    if (child.forAllWindows(callback, traverseTopToBottom)) {        return true;    }}return false;}
    

    forAllWindows 方法会遍历整个容器树都去调用 mUpdateWindowsForAnimator 回调。这个回调内部就会去执行 winAnimator.stepAnimationLocked 去更新 Window 的更新操作。stepAnimationLocked,代表单步动画。这里面的操作大家自行查看也不难。

    这里对动画做个小结,通过在需要动画的时候,post 一个 FrameCallBack 给Choreographer,在 VSync 信号到来的时候,会优先执行动画操作。动画回调内部会去遍历整个容器树模型,依次更改每个 Window 对应的 Surface 的状态。然后在绘制完成后,提交给 SurfaceFlinger。过程图示:

    image

    / 输入事件处理 /

    关于 Input 事件在这篇文章中已经有讲过。输入子系统从驱动文件中读取事件后,再封装提交给 IMS,IMS 再发送给 WMS 进行处理。

    输入系统整体架构:

    image

    今天我们从WMS的角度来分析下输入事件。

    核心类

    InputChannel

    类声明:

    /** * An input channel specifies the file descriptors used to send input events to * a window in another process.  It is Parcelable so that it can be sent * to the process that is to receive events.  Only one thread should be reading * from an InputChannel at a time. * @hide   */    public final class InputChannel implements Parcelable {
    

    注释中说明了 InputChannel 是一个使用文件描述符 fd 来发送 input 事件给其他进程的一个输入通道,且只有一个线程可以同时读取 InputChannel 中的数据,说明 InputChannel 是线程安全的。其内部使用的是 socket 通讯。

    前面在分析 Window 添加过程的时候说过在 WMS 的 addWindow 中会调用。

    win.openInputChannel(outInputChannel)void openInputChannel(InputChannel outInputChannel) {    ...    String name = getName();    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//1    mInputChannel = inputChannels[0];    mClientChannel = inputChannels[1];    mInputWindowHandle.inputChannel = inputChannels[0];    if (outInputChannel != null) {        mClientChannel.transferTo(outInputChannel);//2        mClientChannel.dispose();        mClientChannel = null;    }    ...    mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);//3}InputChannel.java:public static InputChannel[] openInputChannelPair(String name) {    ...    return nativeOpenInputChannelPair(name);}android_view_InputChannel.cpp:static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env...) {    ...    sp<InputChannel> serverChannel;    sp<InputChannel> clientChannel;    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,        std::make_unique<NativeInputChannel>(serverChannel));jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,        std::make_unique<NativeInputChannel>(clientChannel));...env->SetObjectArrayElement(channelPair, 0, serverChannelObj);env->SetObjectArrayElement(channelPair, 1, clientChannelObj);return channelPair;}status_t InputChannel::openInputChannelPair(const String8& name,        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {    int sockets[2];    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {        ...        return result;    }    ...    outServerChannel = new InputChannel(serverChannelName, sockets[0]);    outClientChannel = new InputChannel(clientChannelName, sockets[1]);    return OK;}
    

    通过以上代码可以看出 InputChannel 使用的是 sockets 通讯,且 WindowState 的 openInputChannel 中注释1处:

    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name),
    

    返回的 inputChannels 是一个服务端和客户端的输入通道数组。其中:

    • 下标0表示服务端的 InputChannel
    • 下标1表示客户端的 InputChannel

    在注释3处 registerInputChannel 传入的是 server 端 InputChannel 给 IMS。而注释2处将 client 端的 InputChannel 与 app 端传入的 outInputChannel 关联起来了。

    这样服务端在 InputChannel 就可以写入 input 事件,然后在 app 端的InputChannel 就可以接受到数据了。输入事件通讯模型如下:

    image

    / Surace管理 /

    WMS 负责创建 Surface 以及对 Surface 的摆放工作,之后将 Surface 提交给SurfaceFlinger 进行合并。在 App 层也创建了一个 Surface 对象,但是那个是空对象,用于 WMS 的填充。

    Surface的创建

    Surface 的创建在 WMS 中使用 WindowStateAnimator 代理创建,而WindowStateAnimator 中又创建了一个 WindowSurfaceController 对 Surface 进行管理。

    核心类:WindowSurfaceController

    public WindowSurfaceController(SurfaceSession s, String name, int w, int h...){    mSurfaceControl = new SurfaceControl(s, name, w, h, format, flags, windowType, ownerUid);}public SurfaceControl(SurfaceSession session, String name, ...){    mNativeObject = nativeCreate(session, name, w, h, format, flags,...);}
    

    在其构造方法中创建了一个 SurfaceControl,SurfaceControl 最终进入 native 层,在 native 层创建了一个 Surface,并返回 native surface 地址。实际在 native 层也是创建一个 SurfaceControl。

    static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,        jint windowType, jint ownerUid) {    ```    sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));    SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);    sp<SurfaceControl> surface = client->createSurface(            String8(name.c_str()), w, h, format, flags, parent, windowType, ownerUid);    ...    return reinterpret_cast<jlong>(surface.get());    ```}
    

    那 app 层是什么时候发起 WMS 的 Surface 创建任务的?看 ViewRootImpl 的 relayoutWindow 方法:

    private int relayoutWindow(WindowManager.LayoutParams params..){    int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params...mSurface);}
    

    调用 mWindowSession 的 relayout 方法,并传入最后 mSurface 对象,这是空Surface,在 WMS 中会被填充返回。最终调用到 WMS 中的 relayoutWindow。

    public int relayoutWindow(Session session, IWindow client, int seq..){    ...    result = createSurfaceControl(outSurface, result, win, winAnimator);           }private int createSurfaceControl(Surface outSurface, int result, WindowState win,        WindowStateAnimator winAnimator) {        ```    WindowSurfaceController surfaceController;    try {        surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);    } finally {        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);    }    if (surfaceController != null) {        surfaceController.getSurface(outSurface);//1        if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  OUT SURFACE " + outSurface + ": copied");    }     return result;    ```    }
    

    最终会调用 WindowStateAnimator 的 createSurfaceLocked 这个前面已经分析过了。返回的 surfaceController 对象在注释1处调用 getSurface(outSurface),将 native 层的 Surface 填充到 App 传递过来的 outSurface 进入 getSurface 看看。

    WindowSurfaceController.javavoid getSurface(Surface outSurface) {    outSurface.copyFrom(mSurfaceControl);}Surface.javapublic void copyFrom(SurfaceControl other) {    if (other == null) {        throw new IllegalArgumentException("other must not be null");    }        ```    long surfaceControlPtr = other.mNativeObject;    if (surfaceControlPtr == 0) {        throw new NullPointerException(                "null SurfaceControl native object. Are you using a released SurfaceControl?");    }    long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);        synchronized (mLock) {        if (mNativeObject != 0) {            nativeRelease(mNativeObject);        }        setNativeObjectLocked(newNativeObject);    }    ```}private void setNativeObjectLocked(long ptr) {    if (mNativeObject != ptr) {        mNativeObject = ptr;    }}
    

    getSurface 方法将 WMS 中创建的 WindowSurfaceController 中 SurfaceControl 对象的 mNativeObject 对象传递给新的 Surface,并使用这个对象去 native 层获取一个新的 NativeObject 赋值给当前 Surface 的 mNativeObject,这样 App 层的 Surface 就获取到了 WMS 在 native 中创建的 SurfaceControl 对象,可以在 app 层操作 native 层的 Surface 了。

    那么为什么谷歌要绕这么大圈来创建 Surface 呢?直接在 App 层去创建不就可以了么?

    个人见解谷歌是希望统一管理 Surface 而不是单独让某个应用持有,且 Surface 的摆放操作等都是得由 WMS 进行处理,所以就直接让 WMS 去创建,然后返回给 App 层去绘制 Surface 操作。

    Surface的摆放

    Surface 在创建之后还需要进行屏幕位置的确认,那这个在哪里操作呢?

    核心类:WindowSurfacePlacer

    WMS 在构造的时候就创建了 WindowSurfacePlacer 对象。这个对象主要用来给Surface 进行位置的定位。定位到 WindowSurfacePlacer 的 performSurfacePlacement 方法,这个方法可以说是 WMS 最核心的方法,其负责了所有窗口的摆放工作。如何显示?显示在屏幕什么位置?区域大小等。这些将在确认后,下发给 SurfaceFlinger 进行处理。

    WMS 中任何窗口状态发生改变都会触发该方法,整个方法进行容器树的遍历,确认窗口可见性等。

    final void performSurfacePlacement(boolean force) {        ```    int loopCount = 6;    do {        mTraversalScheduled = false;        performSurfacePlacementLoop();        loopCount--;    } while (mTraversalScheduled && loopCount > 0);    ```    }private void performSurfacePlacementLoop() {    ...    mInLayout = true;    mService.mRoot.performSurfacePlacement(recoveringMemory);    mInLayout = false;    if (mService.mRoot.isLayoutNeeded()) {        if (++mLayoutRepeatCount < 6) {            requestTraversal();        } else {            Slog.e(TAG, "Performed 6 layouts in a row. Skipping");            mLayoutRepeatCount = 0;        }    } else {        mLayoutRepeatCount = 0;    }   }
    

    performSurfacePlacement 最终会调用到 mService.mRoot.performSurfacePlacement, mService.mRoot.performSurfacePlacement 中最终会执行到对窗口容器树做以下遍历操作,中间代码跳转太多,就略过了。主要做了下面三件事:

    1.DisplayContent 的 mPerformLayout 操作。计算所有 DisplayFrame 以及 WindowFrame 的大小和位置。

    private final Consumer<WindowState> mPerformLayout = w -> {    ...    mService.mPolicy.layoutWindowLw(w, null);    ...}mPolicy = PhoneManagerPolicyPhoneManagerPolicy.javapublic void layoutWindowLw(WindowState win, WindowState attached) {    //这里面都是对不同给的Window的位置进行确认    computeFrameLw(....);//这个方法会计算所有DisplayFrame以及WindowFrame的大小和位置}
    

    2.DisplayContent 的 mApplySurfaceChangesTransaction 操作。

    private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {    ..前面一大推处理    winAnimator.setSurfaceBoundariesLocked(mTmpRecoveringMemory /* recoveringMemory */);  }  winAnimator = WindowStateAnimator  WindowStateAnimator.java  void setSurfaceBoundariesLocked(final boolean recoveringMemory) {    calculateSurfaceBounds(w, attrs);//1    mSurfaceResized = mSurfaceController.setSizeInTransaction(                      mTmpSize.width(), mTmpSize.height(), recoveringMemory);//2    ...    mSurfaceController.setPositionInTransaction((float) Math.floor(posX),                      (float) Math.floor(posY), recoveringMemory);//3}
    

    注释1处计算 Surface 的 size 大小,然后在注释2处使用 mSurfaceController 设置到 native 层的 SurfaceController 对象中, 注释3处在计算好位置后,也使用 mSurfaceController 设置到 native 层的 SurfaceController 对象中。这样就将 Surface 在屏幕中给的位置以及大小都确认下来了。

    3.WindowStateAnimator的commitFinishDrawingLocked();提交事务。

    boolean commitFinishDrawingLocked() {    ```    mDrawState = READY_TO_SHOW;    boolean result = false;    final AppWindowToken atoken = mWin.mAppToken;    if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {        result = mWin.performShowLocked();    }    return result;    ```}boolean performShowLocked() {    final int drawState = mWinAnimator.mDrawState;    if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW)            && mAttrs.type != TYPE_APPLICATION_STARTING && mAppToken != null) {        mAppToken.onFirstWindowDrawn(this, mWinAnimator);    }    ```    if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {        return false;    }        logPerformShow("Showing ");        mService.enableScreenIfNeededLocked();    mWinAnimator.applyEnterAnimationLocked();        mWinAnimator.mDrawState = HAS_DRAWN;    mService.scheduleAnimationLocked();//1        if (mHidden) {        mHidden = false;        final DisplayContent displayContent = getDisplayContent();          //2        for (int i = mChildren.size() - 1; i >= 0; --i) {            final WindowState c = mChildren.get(i);            if (c.mWinAnimator.mSurfaceController != null) {                c.performShowLocked();            }        }    }    ```        }    ````
    

    performShowLocked 方法中有大量的对窗口状态的判断,窗口的显示过程共有五个状态。

    NO_SURFACE

    在创建 WindowState 后的默认状态,表示当前窗口还创没有执行 relayout() 方法创建 Surface。

    DRAW_PENDING

    执行 relayout() 方法后,创建完成 Surface 后的状态,表示等待绘制。

    COMMIT_DRAW_PENDING

    窗口 Surface 上完成绘制后的状态,执行WindowStateAnimator#finishDrawingLocked() 方法设置,表示已经完成绘制,等待下次刷帧进行提交。

    READY_TO_SHOW

    表示窗口已经绘制完成并且完成提交,此时如果该窗口的兄弟窗口全部完成绘制且满足显示要求,则直接进行 HAS_DRAWN 的转变完成显示,否则等待其他兄弟窗口完成绘制后,再进行 HAS_DRAWN 转变。

    HAS_DRAWN

    表示该窗口正式显示。

    在注释2处又对窗口容器树进行了遍历,都指向 performShowLocked 方法。在注释1处调用了 WMS 的 scheduleAnimationLocked 方法,如果你还有印象,在前面分析窗口动画的时候说过,scheduleAnimationLocked 方法会将动画帧回调 FrameCallback 设置到 Choreographer 中去。然后在 VSYNC 信号到来的时候,指向 CallBack 动画回调、最后执行 animate。

    private void animate(long frameTimeNs) {    //..    for (int i = 0; i < numDisplays; i++) {        final int displayId = mDisplayContentsAnimators.keyAt(i);        final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);        ...        dc.prepareWindowSurfaces();}void prepareWindowSurfaces() {    forAllWindows(mPrepareWindowSurfaces, false / traverseTopToBottom */);}private final Consumer<WindowState> mPrepareWindowSurfaces =        w -> w.mWinAnimator.prepareSurfaceLocked(true);void prepareSurfaceLocked(final boolean recoveringMemory) {    boolean prepared = mSurfaceController.prepareToShowInTransaction(mShownAlpha,..);//1        ```    mSurfaceController.setLayer(mAnimLayer);//2        showSurfaceRobustlyLocked()//3    ```}private boolean showSurfaceRobustlyLocked() {    final Task task = mWin.getTask();    if (task != null && StackId.windowsAreScaleable(task.mStack.mStackId)) {        mSurfaceController.forceScaleableInTransaction(true);    }boolean shown = mSurfaceController.showRobustlyInTransaction();    if (!shown)        return false;        if (mWin.mTurnOnScreen) {        if (DEBUG_VISIBILITY) Slog.v(TAG, "Show surface turning screen on: " + mWin);        mWin.mTurnOnScreen = false;        mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;    }    return true;    ```}
    

    最终在 showSurfaceRobustlyLocked 中调mSurfaceController.showRobustlyInTransaction() 方法进行 Surface 的提交给 SurfaceFlinger 进行合成并显示在屏幕上。

    可以看到 Surface的size,postion 以及状态管理,提交执行等操作还是一个比较繁琐的过程。

    时序图如下(原图可以去原文查看):

    image image

    以上是Android开发中,以及在车载开发之中起到很重要的地位。有关更多Android开发核心技术以及车载开发技术学习;大家可以点击参考《Android核心技术手册》查看更多学习资料。

    / 总结 /

    WMS在系统中的几大职责

    image

    相关文章

      网友评论

        本文标题:Android开发技术——车机技术之WMS学习

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