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

news2024/11/14 19:51:17

/ 窗口管理 /

窗口管理核心类介绍

窗口管理使用到的 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 关系如下:

WindowState

类声明:

class WindowState extends WindowContainer<WindowState>

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

如何查看当前设备 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 的键值对集合中。三种关系可以简单总结如下:

/ 窗口动画 /

我们知道在 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。过程图示:

/ 输入事件处理 /

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

输入系统整体架构:

今天我们从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 就可以接受到数据了。输入事件通讯模型如下:

/ 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 以及状态管理,提交执行等操作还是一个比较繁琐的过程。

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

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

/ 总结 /

WMS在系统中的几大职责

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/188188.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

免费好用的IPv6之远程管理路由器-OpenWrt上uhttpd的TLS(HTTPS)部署流程介绍

在免费好用的IPv6之远程管理路由器-OpenWrt上uhttpd的使用介绍_123-wqy的博客-CSDN博客_openwrt uhttpd这篇文章的最后&#xff0c;我们已经可以使用80、443端口从路由器内外网的IPv6地址对路由器的WEB界面进行访问。但是&#xff0c;考虑外网访问场景下的安全性问题&#xff0…

初级软件测试面试会问什么 这些问题你都知道吗?

在现如今这竞争十分激烈的软件测试职场中&#xff0c;求职者们想获得一份让自己满意且高薪的工作是十分不易的&#xff0c;因此&#xff0c;只有事先做好充分准备才能让自己通关筹码加倍。在这里&#xff0c;我给各位即将踏入面试征程的初级软件测试小伙伴们准备了一些关于初级…

车载网络测试 - BootLoader刷写 - 总纲

随着车载以太网技术的快速发展&#xff0c;智能汽车也已经走进了千家万户&#xff0c;OTA无线解决方案也逐渐走进了大众的视野&#xff1b;实际上在车载以太网未出现之前&#xff0c;我们车上大多数使用的升级一般都是adb、U盘等不太方便的刷写方式&#xff0c;并且在车上是一种…

字节青训营——架构初探学习笔记

1. 规则引擎 规则引擎是一种嵌入在应用服务中的组件&#xff0c;可以将灵活多变的业务决策从服务代码中分离出来。通过使用预定义的语义模块来编写业务逻辑规则。在执行时接受数据输入、解释业务规则&#xff0c;并做出决策。规则引擎能大大提高系统的灵活性和扩展性。 在字节…

小波神经网络的时间序列预测模型

一、小波变换 波变换主要通过伸缩和平移实现多尺度细化&#xff0c;突出所要处理的问题细节&#xff0c;有效提取局部信息。 将傅里叶变换中无限长的三角函数基换成了有限长的会衰减的小波基。 小波变换不仅可以知道信号的频率成分&#xff0c;还能知道各频率成分出现的时刻…

Win10蓝屏问题:SYSTEM_THREAD_EXCEPTION_NOT_HANDLED

最近一段时间&#xff0c;我一直出现蓝屏的情况&#xff0c; 以为是CPU散热不行导致的重启&#xff0c; 因为比较频繁所以在此记录解决这个问题&#xff0c; 还是一贯的风格&#xff1a;图文并茂。终止代码&#xff1a;SYSTEM_THREAD_EXCEPTION_NOT_HANDLED失败的操作&#xff…

关于灰度发布基本问题的解答及轻量化落地方案

由于工作需要&#xff0c;近期又恶补了一下“灰度发布”的相关知识&#xff0c;也和身边小伙伴探讨了轻量化实现灰度发布的落地方案。借此机会&#xff0c;正好将相关内容跟大家整理分享一下。 什么是灰度&#xff1f; 要想了解这个问题就要先明白什么是灰度。灰度从字面意思…

BGP综合实验(华为)

题目&#xff1a; 思路&#xff1a; 把路由划分在AS1,AS2,AS3之中&#xff0c;并使各网段之间的网络互通。明确交接端的路由的路由宣告在哪个区域&#xff0c;例如R1宣告在1.1.1.0 24 网段中&#xff08;易于识别的网段&#xff09;R1对应1.1.1.0网段。其他的路由器以此推类。利…

gma 地理空间绘图:(1)绘制简单的世界地图-1.地图绘制与细节调整

了解 gma gma 是什么&#xff1f; gma 是一个基于 Python 的地理、气象数据快速处理和数据分析函数包&#xff08;Geographic and Meteorological Analysis&#xff0c;gma&#xff09;。gma 网站&#xff1a;地理与气象分析库。 gma 的主要功能有哪些&#xff1f; 气候气象&a…

基于大数据的公共建筑能耗监测系统的应用探究

摘要&#xff1a;为了解决当前公共建筑能耗居高不下的突出问题&#xff0c;借助当前信息化技术手段&#xff0c;围绕公共建筑能耗监测系统中的大数据应用&#xff0c;从监测系统的总设计框架入手&#xff0c;分别就物联网中数据采集器设计方式、数据传输技术、数据库部署方式分…

【浪漫情人节】送你Python表白神器,祝天下有情人终成眷属

哈哈哈再过十几天就到了一年一度的情人节啦&#xff01;如此浪漫的日子&#xff0c;小王决定用Python写一个简单的表白神器送给大家&#xff0c;祝天下有情人终成眷属&#xff01; 目录 前言 一、Turtle小海龟 1. 基本函数 2. 漂浮爱心 二、Tkinter界面设计 1. 基本…

微信小程序015中医知识库百科科普系统

本系统为网上用户提供了一个在线浏览知识的平台。主要实现了用户登陆注册、在线浏览知识的功能&#xff0c;用户可以将自己的拥有的知识信息上传到中医药知识库系统中以供其他用户在线浏览并评论。同时实现了中医药知识库系统的后台管理功能&#xff0c;提供了管理员和操作员权…

06 Alibaba微服务组件Sentinel (1)

1、分布式系统遇到的问题 服务的可用性问题 服务的可用性场景 在一个高度服务化的系统中,我们实现的一个业务逻辑通常会依赖多个服务, 如图所示: 如果其中的下单服务不可用, 就会出现线程池里所有线程都因等待响应而被阻塞, 从而造成整个服务链路不可用&#xff0c; 进而导致…

深度解锁 CRUD 应用开发平台

全文 2400 字 阅读时间约 8 分钟 本文首发于码匠技术博客 目录 使用 CRUD 应用开发平台的好处 CRUD 应用开发平台的主要功能 如何使用 CRUD 应用开发平台&#xff1f; 如何自定义 CRUD 应用开发平台&#xff1f; 使用码匠开发的 CRUD 应用程序类型 关于码匠 CRUD 应用是具…

tomcat多实例优化及zabbix监控群集

tomcat简介Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目&#xff0c;由Apache,Sun和其他一些公司及个人共同开发而成。Tomcat服务器是一个免费的开放源代码的Web应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和…

LeetCode——2315. 统计星号

一、题目 给你一个字符串 s &#xff0c;每两个连续竖线 ‘|’ 为 一对 。换言之&#xff0c;第一个和第二个 ‘|’ 为一对&#xff0c;第三个和第四个 ‘|’ 为一对&#xff0c;以此类推。 请你返回不在 竖线对之间&#xff0c;s 中 ‘*’ 的数目。 注意&#xff0c;每个竖…

python flask 操作 Redis,包含详细代码

使用 Flask 操作 Redis 可以通过安装 Redis 的 Python 库并在 Flask 中使用它来实现。 正式学习前&#xff0c;需要先确定 Redis 已安装并可正常运行 运行 redis-server.exe redis.windows.conf 启动 Redis 服务器 运行 redis-cli.exe ping 检查 Redis 服务器是否运行正常 运行…

IP地址扫描程序/扫描工具

随着许多组织采用自带设备 &#xff08;BYOD&#xff09; 等策略&#xff0c;IT 网络变得越来越复杂。随着越来越多的设备连接到网络&#xff0c;IP 地址扫描势在必行&#xff0c;以确保设备在连接到网络时不会遇到问题。IP 地址扫描器是 OpUtils 中的一个模块&#xff0c;是一…

Windows包管理工具winget

文章目录简介实操简介 在Win10或者Win11中默认安装了winget工具&#xff0c;可以直接在命令行中输入命令&#xff0c;如果有反应&#xff0c;说明已经安装。 >winget -v v1.4.10173输入winget可以查看命令列表 命令说明show显示包的相关信息install安装给定的程序包unins…

几何对象基本元素与表现

点与向量 点 (point) 表示空间中的位置&#xff0c;它有空间中的坐标&#xff0c;例如在三维空间中&#xff0c;点有 (x, y, z) 坐标。 向量 (vector) 是有方向的量&#xff0c;它可以表示在空间中的移动。向量可以通过两个点的差计算&#xff0c;它连接起始点和终止点。 结…