Android U WMS : 屏幕旋转动画(3)

Android U WMS: 屏幕旋转动画(2) 之 app 侧 在 app 侧分析了 relaunch activity 的生命周期流程。但是,我省略了 ViewRoomtImpl 相关的代码的分析,因为这将浪费大量的篇幅在 app 侧,从而推迟了 WMS 侧的旋转动画的分析。

ViewRootImpl 与 WMS 交互的流程,我相信很多人都有所了解,只是不清楚细节。但是,对于理解旋转动画流程来说,就够了。

要想在 WMS 的代码海洋中遨游,必须得分清主次,否则容易迷失方向。因此,本文会省略那些对旋转动画关系不大的代码,旨在更清晰地表达旋转动画的执行流程。

remove window

relaunch activity 的 destroy activity 流程,通过 WindowManager 移除 MainActivity 的 DecorView,而 ViewRootImpl 会通知 WMS 移除窗口

java 复制代码
// WindowManagerService.java

void removeWindow(Session session, IWindow client) {
    synchronized (mGlobalLock) {
        // 找到对应的窗口 WindowState
        WindowState win = windowForClientLocked(session, client, false);
        if (win != null) {
            // 由 WindowState 来完成移除窗口
            win.removeIfPossible();
            return;
        }

        // ...
    }
}
java 复制代码
// WindowState.java

void removeIfPossible() {
    // ...

    try {
        // ...

        removeImmediately();
        
        // ...
        
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}
java 复制代码
// WindowState.java

void removeImmediately() {
    // ...

    mRemoved = true;
    
    
    // 1. 销毁用于 app 绘制的 surface
    mWinAnimator.destroySurfaceLocked(getSyncTransaction());
    
    // ...
    
    // 2. 把 WindowState 从 ActivityRecord 中移除
    super.removeImmediately();

    // ...

    // 3.WMS 执行窗口移除后的清理工作
    mWmService.postWindowRemoveCleanupLocked(this);
}

窗口的移除由 WindowState#removeImmediately() 完成,主要过程如下

  1. 使用 sync transcation,通过WindowStateAnimator#destroySurfaceLocked() 销毁用于 app 绘制的 surface。参考【移除 app 绘制的 surface
  2. 使用 sync transaction 移除 WindowState 的 surface,并且从父容器 ActivityRecord 中移除 WindowState。参考【移除 WindowState
  3. 通知 WMS 做窗口移除后的清理工作。这与旋转动画关系不大,就不做分析了。

根据第一篇文章的分析可知,MainActivity 的 WindowState 此时处于同步状态,因此通过它的 getSyncTransaction() 获取的一定是 sync transaction,后面将不再重复说明。

移除 app 绘制的 surface

java 复制代码
// WindowStateAnimator.java

// t 是 WindowState 的 sync transaction
void destroySurfaceLocked(SurfaceControl.Transaction t) {
    // ...

    // 设置 WindowState#mHidden 为 true
    mWin.mHidden = true;

    try {
        ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
                mWin, new RuntimeException().fillInStackTrace());
        
        destroySurface(t);

        // ...
    } catch (RuntimeException e) {}

    // ...
}


// t 此时是 sync transaction
void destroySurface(SurfaceControl.Transaction t) {
    try {
        if (mSurfaceController != null) {
            // 由 WindowSurfaceController 销毁 surface
            mSurfaceController.destroy(t);
        }
    } catch (RuntimeException e) {
    } finally {
        // WindowState#mHasSurface 设置为 false,表明窗口没有 surface
        mWin.setHasSurface(false);
        // WindowStateAnimator#mSurfaceController 设置为 null
        // 窗口没有 surface,自然不需要 surface 控制器
        mSurfaceController = null;
        // 绘制状态 WindowStateAnimator#mDrawState 设置为 NO_SURFACE
        mDrawState = NO_SURFACE;
    }
}

WindowStateAnimator 使用 WindowSurfaceController 管理 app 绘制的 surface,因此销毁的工作,也是由 WindowSurfaceController 来完成,其实就是用 WindowState 的 sync transcation 移除 surface,如下

java 复制代码
// WindowSurfaceController.java

// t 此时是 sync transaction
void destroy(SurfaceControl.Transaction t) {
    ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
            "Destroying surface %s called by %s", this, Debug.getCallers(8));
    try {
        if (mSurfaceControl != null) {
            // ...
            
            // 通过 sync transaction 移除 surface
            t.remove(mSurfaceControl);
        }
    } catch (RuntimeException e) {
    } finally {
        setShown(false);
        mSurfaceControl = null;
    }
}

app 绘制的 surface 被销毁后,其实我们还要关心下一些状态的更新

  1. WindowState#mHidden 应该表示 app 绘制的 surface 是否处于 hidden 状态。如果 surface 都被移除了,肯定要设置为 true。
  2. WindowState#mHasSurface 应该表示 WindowState 是否还拥有 app 绘制的 surface。如果 surface 被销毁了,肯定要设置为 true。
  3. WindowStateAnimator#mDrawState 代表窗口的绘制状态。如果用于 app 绘制的 surface 都被销毁了,那么这个状态会被重置为 NO_SURFACE

移除 WindowState

移除 app 绘制的 surface 后,现在轮到移除 WindowState 自己的 surface,以及从 WMS 窗口层级上移除 WindowState,如下

java 复制代码
// WindowContainer.java

void removeImmediately() {
    // ...
    
    // 这里是递归移除 WC 的所有 children
    // 对于 WindowState 来说,这里就是递归移除所有的子窗口
    // 本文不考虑子窗口情况
    while (!mChildren.isEmpty()) {
        // ...
    }

    if (mSurfaceControl != null) {
        // 1.通过 sync transcation 移除 WC 的 surface
        getSyncTransaction().remove(mSurfaceControl);
        
        // 把 WindowState#mSurfaceControl 设置为 null,表示 WindowState 不再拥有 surface
        setSurfaceControl(null);
        // 重置 mLastSurfacePosition,mLastDeltaRotation
        mLastSurfacePosition.set(0, 0);
        mLastDeltaRotation = Surface.ROTATION_0;
        
        scheduleAnimation();
    }
    
    // ...

    // 2.通知 parent 移除 child
    // Activity 的 WindowState 的 parent 就是 ActivityRecord
    if (mParent != null) {
        mParent.removeChild(this);
    }

    // ...
}

使用 sync transaction 移除了 WindowState 的 surface,这就是从底层上断开了 surface 之间的关联。 然后,再从窗口层级上进行操作,从 ActivityRecord 中移除 WindowState,如下

java 复制代码
// ActivityRecord.java

void removeChild(WindowState child) {
    // ...
    
    super.removeChild(child);
    
    // ...
}
java 复制代码
// WindowContainer.java

void removeChild(E child) {
    // 1.从集合 mChildren 中移除 child
    if (mChildren.remove(child)) {
        // 注意,对于 ActvityRecord 来说,只更新了 tree weight
        // ActivityRecord#mVisibleRequested 不由 children 决定
        onChildRemoved(child);
        
        // 2.child 设置 parent 为 null
        if (!child.mReparenting) {
            child.setParent(null);
        }
    }
    // ...
}

从 ActivityRecord 的集合 mChildren 中移除 WindowState,然后把 WindowState 的 parent 设置为 null。这样就从 WMS 窗口层级上,断开了 ActivityRecord 与 WindowState 之间的关联。

WindowState 对 surface 的操作,都集中在自己的 sync transaction 上。现在 WindowState 已经完全移除了,那么它的 sync transcation 怎么处理呢?不能直接丢弃吧,否则会丢失 surface 的操作。系统选择合并到 parent ActivityRecord 的 sync transaction 中,这个操作隐藏在 WindowState 设置 parent 为 null 的过程中,如下

java 复制代码
// WindowContainer.java

// 参数 parent 为 null
final protected void setParent(WindowContainer<WindowContainer> parent) {
    final WindowContainer oldParent = mParent;
    // 1.保存新 parent,此时为 null
    mParent = parent;

    if (mParent != null) {
    
    } else if (mSurfaceAnimator.hasLeash()) {
    
    }
    
    if (!mReparenting) {
        // 2. reparent 时进行同步
        onSyncReparent(oldParent, mParent);
        if (mParent != null && mParent.mDisplayContent != null
                && mDisplayContent != mParent.mDisplayContent) {
                
        }
        // 由于新 parent 为 null,所以这里什么也没做
        onParentChanged(mParent, oldParent);
    }
}
java 复制代码
// WindowContainer.java

private void onSyncReparent(WindowContainer oldParent, WindowContainer newParent) {
    // ...

    if (newParent == null || newParent.mSyncState == SYNC_STATE_NONE) {
        if (mSyncState == SYNC_STATE_NONE) {
            return;
        }
        if (newParent == null) {
            // This is getting removed.
            if (oldParent.mSyncState != SYNC_STATE_NONE) {
                // In order to keep the transaction in sync, merge it into the parent.
                // 被移除了,直接完成同步
                finishSync(oldParent.mSyncTransaction, getSyncGroup(), true /* cancel */);
            } // ...
            return;
        } // ...
    }
    // ...
}
java 复制代码
// WindowState.java

void finishSync(Transaction outMergedTransaction, BLASTSyncEngine.SyncGroup group,
        boolean cancel) {
    // ...
    
    // 通过父类函数,把自己的 sync transcation 合并到 ActivityRecord 的 sync transcation
    super.finishSync(outMergedTransaction, group, cancel);
}
java 复制代码
// WindowContainer.java

// outMergedTransaction 此时是 ActivityRecord 的 sync transaction
// cancel 为 true
void finishSync(Transaction outMergedTransaction, BLASTSyncEngine.SyncGroup group,
        boolean cancel) {
    // ...
    
    ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "finishSync cancel=%b for %s", cancel, this);
    
    // 合并 sync transcation
    // outMergedTransaction 此时是 ActivityRecord 的 sync transaction
    outMergedTransaction.merge(mSyncTransaction);
    
    // 递归地合并 children 的 sync transaction
    // 对于 WindowState 来首,合并的是子窗口 sync transaction
    for (int i = mChildren.size() - 1; i >= 0; --i) {
    
    }
    
    // 参数 cancel 此时为 true,这里会通知 SyncGroup 从 mRootMembers 中移除自己
    // WindowState 本来就不在 mRootMembers 中
    if (cancel && mSyncGroup != null) mSyncGroup.onCancelSync(this);
    
    // 重置同步相关的数据
    mSyncState = SYNC_STATE_NONE;
    mSyncMethodOverride = BLASTSyncEngine.METHOD_UNDEFINED;
    mSyncGroup = null;
}

还记得前面的分析吗? WindowState 的 sync transaction 保存了哪些操作? 有移除 app 绘制 surface 的操作,还有移除自己 surface 的操作。而现在这些操作,都保存到了旧 parent ActivityRecord 的 sync transaction 中。

add window

relaunch activity 的 resume activity 流程,会通过 WindowManager 添加 DecorView,而 ViewRootImpl 会向 WMS 添加窗口

java 复制代码
// WindowManagerService.java

public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
        int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,
        InputChannel outInputChannel, InsetsState outInsetsState,
        InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
        float[] outSizeCompatScale) {
    // ...

    synchronized (mGlobalLock) {
        // ...

        
        // 获取窗口相关的 WindowToken
        WindowToken token = displayContent.getWindowToken(
                hasParent ? parentWindow.mAttrs.token : attrs.token);
        

        // 1.创建 WindowState
        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], attrs, viewVisibility, session.mUid, userId,
                session.mCanAddInternalSystemWindow);
        
        // ...

        if (res != ADD_OKAY) {
            return res;
        }

        // attrs.inputFeatures 顾名思义,跟 input 相关
        // attrs.inputFeatures 此时为 0,代表要窗口输入通道
        final boolean openInputChannels = (outInputChannel != null
                && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
        if  (openInputChannels) {
            win.openInputChannel(outInputChannel);
        }


        // ...

        // From now on, no exceptions or errors allowed!

        res = ADD_OKAY;

        // 默认是使用 BLAST
        if (mUseBLAST) {
            res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
        }

        // ...

        // 2. attach WindowState
        // 主要是创建 SurfaceSessiion
        win.attach();
        
        // 3. WindowManagerService#mWindowMap
        mWindowMap.put(client.asBinder(), win);
        
        // ...

        // 4. WindowToken 保存 WindowState
        win.mToken.addWindow(win);
        
        // ...


        if (displayContent.isInTouchMode()) {
            res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
        }
        
        if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
            res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
        }
        
        // ...

        // 5.更新 WindowState 的 layer
        win.getParent().assignChildLayers();
        
        // ...
    }

    Binder.restoreCallingIdentity(origId);

    return res;
}

WMS 的 add window 流程

  1. 创建 WindowState。在 WMS 端,WindowState 代表一个窗口,但是 WindowState surface 并不是 app 绘制的 surface。
  2. WindowState attach,主要是在 window session 中创建 surface session,与 SF 建立连接。对于 relaunch activity 流程来说,由于进程并没有重启,app 端与 WMS 端建立的 window session 并没有重新创建,因此不会再去创建 surface session。
  3. WindowManagerService#mWindowMap 以客户端传入的 IWindow 为 KEY,保存 WindowState。
  4. ActivityRecord 保存 WindowState。这里不仅在窗口层级上,建立 ActivityRecord 与 WindowState 的关联。还会在底层 surface 上,为 WindowState 创建 surface ,并挂载到 ActivityRecord 的 surface 之下。参考【ActivityRecord 保存 WindowState】。
  5. 通知 WindowState 的 parent ActivityRecord 更新 child layer。需要注意的是,这里是通过 ActivityRecord 的 sync transcation 更新 layer 的。

ActivityRecord 保存 WindowState

java 复制代码
// ActivityRecord.java

void addWindow(WindowState w) {
    // 通过父类完成窗口的添加
    super.addWindow(w);
    
    // ...
}
java 复制代码
// WindowToken.java

void addWindow(final WindowState win) {
    ProtoLog.d(WM_DEBUG_FOCUS,
            "addWindow: win=%s Callers=%s", win, Debug.getCallers(5));

    // ...
    
    if (!mChildren.contains(win)) {
        ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
        
        // 保存 child
        addChild(win, mWindowComparator);
        
        // ...
    }
}
java 复制代码
// WindowContainer.java


protected void addChild(E child, Comparator<E> comparator) {

    // 寻找要添加的 index
    int positionToAdd = -1;
    if (comparator != null) {
        final int count = mChildren.size();
        for (int i = 0; i < count; i++) {
            if (comparator.compare(child, mChildren.get(i)) < 0) {
                positionToAdd = i;
                break;
            }
        }
    }

    // 根据index,保存到集合 mChildren 
    if (positionToAdd == -1) {
        mChildren.add(child);
    } else {
        mChildren.add(positionToAdd, child);
    }

    
    // 给 child 设置 parent
    child.setParent(this);
}

ActivityRecord 通过集合 mChildren 保存了 child WindowState,然后 child WindowState 保存 parent ActivityRecord。这样就在 WMS 窗口层级上,建立了 ActivityRecord 与 WindowState 的关联。但是这还不够,还需要建立 surface 之间的关联,这是在 WindowState 设置 parent 的过程中实现的

java 复制代码
// WindowContainer.java

// parent 为 ActivityRecord
final protected void setParent(WindowContainer<WindowContainer> parent) {
    final WindowContainer oldParent = mParent;
    // 1. 保存 parent
    mParent = parent;

    if (mParent != null) {
        // 2.通知 parent 处理 child 添加完成
        mParent.onChildAdded(this);
    } else if (mSurfaceAnimator.hasLeash()) {
        
    }
    if (!mReparenting) {
        // 3. 在 reparent 时进行同步
        onSyncReparent(oldParent, mParent);
        if (mParent != null && mParent.mDisplayContent != null
                && mDisplayContent != mParent.mDisplayContent) {
            // 4. child 处理 DisplayContent 改变
            // 主要更新 mDisplayContent
            onDisplayChanged(mParent.mDisplayContent);
        }
        // 4. child 处理 parent 改变
        onParentChanged(mParent, oldParent);
    }
}

代入 WindowState 的角色,结合基类函数 WindowContainer#setParent(),分析了 set parent 的过程

  1. WindowState 通过 mParent 保存 ActivityRecord。
  2. ActivityRecord 处理 child 添加完成。对于 ActivityRecord 来说,它只更新了 tree weigh,而 ActivityRecord#mVisibleRequested 并不由 child 添加和移除决定
  3. WindowState 在 reparent 时,进行同步准备。根据第一篇文章的分析,ActivityRecord 处于同步状态,因此添加 WindowState 时,WindowState 也必须做同步准备。参考【添加WindowState的同步准备
  4. WindowState 处理配置 parent 改变。WindowState 会创建自己的 surface,然后挂载到 ActivityRecord surface 之下。参考【child 处理 parent 改变

添加WindowState的同步准备

java 复制代码
// WindowContainer.java

private void onSyncReparent(WindowContainer oldParent, WindowContainer newParent) {

    // ...

    if (mTransitionController.isShellTransitionsEnabled()) {
        mSyncState = SYNC_STATE_NONE;
        mSyncMethodOverride = BLASTSyncEngine.METHOD_UNDEFINED;
    }
    
    // 准备同步
    prepareSync();
}

prepareSync() 在第一篇文章中分析过,它会把 WindowState 的同步状态切换到 SYNC_STATE_WAITING_FOR_DRAW,它表示动画需要等待窗口绘制完成。

child 处理 parent 改变

java 复制代码
// WindowContainer.java

void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
    // 1. 通过父类 ConfigurationContainer 处理 parent 改变
    // ConfigurationContainer 只会处理配置改变
    super.onParentChanged(newParent, oldParent);
    
    if (mParent == null) {
        return;
    }

    if (mSurfaceControl == null) {
        // 2. 创建 surface
        createSurfaceControl(false /*force*/);
    } else {
    }
    
    // 3.通知 parent 更新 child layer
    mParent.assignChildLayers();
}

WindowState 通过基类 WindowContainer 处理 parent 改变

  1. 通过父类 ConfigurationContainer 处理 parent 改变。 从看到 ConfigurationContainer 那一刻起,就应该明白,它只会处理配置。这里就是根据 ActivityRecord 的 full config 更新 WindowState 的配置。在第一篇文章中,详细分析了配置时如何更新的,这里就不再展开。
  2. 创建 WindowState 自己的 surface,并且还会挂载到 ActivityRecord 的 surface 之下。
  3. 通知 WindowState 的 parent ActivityRecord 更新它的 children 的 layer。一般来说,child 在集合 mChildren 中的索引越大,surface layer 的值就越大,surface 就会显示在越上面。再次提醒,这个仍然是在 sync transaction 中执行的。

现在来看下,WindowState 的 surface 是如何创建和挂载到 ActivityRecord surface 之下的

java 复制代码
// WindowContainer.java

void createSurfaceControl(boolean force) {
    setInitialSurfaceControlProperties(makeSurface());
}

Builder makeSurface() {
    // 1.通过 parent 的makeChildSurface 来创建 SurfaceControl.Builder
    final WindowContainer p = getParent();
    return p.makeChildSurface(this);
}

void setInitialSurfaceControlProperties(Builder b) {
    // 2. 通过 SurfaceControl.Builder 的 create() 创建 SurfaceControl
    // 然后保存到 mSurfaceControl
    setSurfaceControl(b.setCallsite("WindowContainer.setInitialSurfaceControlProperties")
                 .build());
    
    // WindowState的 surface 在创建时候,会立即 show
    // 而 AcivityRecord 不会
    if (showSurfaceOnCreation()) {
        // 3.使用的 sync transaction show surface
        getSyncTransaction().show(mSurfaceControl);
    }
    
    // ...
}

从这里可以看到一个 WC 创建 surface 的标准逻辑

  1. 通过 parent 的 makeChildSurface() 创建 SurfaceControl.Builder。
  2. 通过 SurfaceControl.Builder 的 create() 创建 SurfaceControl,然后保存到 WC 的 mSurfaceControl 变量中。这样,WC 就有自己的 surface 了。
  3. 如果需要再创建就show出来,那么会通过 sync transcation 进行 show。对于 WindowState 来说,是需要在创建 surface 就 show surface 的。

来看下 WindowState 的 parent 是如何创建 SurfaceControl.Builder 的,由于 ActivityRecord 并未实现 makeChildSurface(),因此仍然使用基类的函数

java 复制代码
// WindowContainer.java

Builder makeChildSurface(WindowContainer child) {
    final WindowContainer p = getParent();
    return p.makeChildSurface(child)
            // 注意了,这里设置了 parent surface
            // 也就是把 surface 挂载到 parent surface 下
            .setParent(mSurfaceControl);
}

从这里要注意一点,child 利用 parent 创建 SurfaceControl.Builder 的过程时,通过参数保存了 surface parent。也就是说,WindowState 的 surface 最终会挂载到 ActivityRecord surface 之下。

目前,只有 DisplayContent 实现了创建 SurfaceControl.Builder

java 复制代码
// DisplayContent.java

SurfaceControl.Builder makeChildSurface(WindowContainer child) {
    SurfaceSession s = child != null ? child.getSession() : getSession();
    
    // 创建 SurfaceControl.Builder
    // 设置为 container 类型 layer
    final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s).setContainerLayer();
    if (child == null) {
        return b;
    }

    // SurfaceControl 的名字设置为 child 的名字
    return b.setName(child.getName())
            // 这里的 mSurfaceControl 是 DisplayContent 的,但是最终的 parent 是其直接 parent
            .setParent(mSurfaceControl);
}

因此 DisplayContent 下所有 WC 创建 surface,都是利用 DisplayContent 创建的,只不过 WC 的 surface 最终只会挂载到其容器 parent 的 surface 之下。

relayout window

relaunch activity 的 resume activity 流程,会通过 WindowManager 添加 DecorView,而 ViewRootImpl 会向 WMS 发起 relayout window

java 复制代码
// WindowManagerService.java

public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
        int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
        int lastSyncSeqId, ClientWindowFrames outFrames,
        MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
        InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
        Bundle outSyncIdBundle) {
    
    synchronized (mGlobalLock) {
        // ...

        // We should only relayout if the view is visible, it is a starting window, or the
        // associated appToken is not hidden.
        final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                        || win.mActivityRecord.isClientVisible());

        // ...

        if (shouldRelayout && outSurfaceControl != null) {
            try {
                // 1. 创建 surface,并复制给 app 端的 outSurfaceControl
                result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
            } catch (Exception e) {
                // ...
            }
        }

        // 2. 发起一次窗口大刷新
        mWindowPlacerLocked.performSurfacePlacement(true /* force */);

        // ...

        if (outFrames != null && outMergedConfiguration != null) {
            // 3. 填充 outFrames 和 outMergedConfiguration
            win.fillClientWindowFramesAndConfiguration(outFrames, outMergedConfiguration,
                    false /* useLatestConfig */, shouldRelayout);

            // ...
        }

        // ...
    }

    Binder.restoreCallingIdentity(origId);
    return result;
}

relayout window 主要需要完成三件大事,第一是给 app 创建用于绘制的 surface,第二是 layout window,计算窗口的位置和大小,第三是把窗口的数据返回给 app 端。

本文把重点放在创建 surface 上,因为这关乎窗口的状态,而窗口的状态与动画有关

java 复制代码
// WindowManagerService.java

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

    WindowSurfaceController surfaceController;
    try {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
        // 由 WindowStateAnimator 创建 WindowSurfaceController
        // WindowSurfaceController 会创建用于 app 绘制的 surface
        surfaceController = winAnimator.createSurfaceLocked();
    } finally {
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
    
    
    if (surfaceController != null) {
        // 把底层的 surface handle 复制给 app 端
        surfaceController.getSurfaceControl(outSurfaceControl);
        ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
    } else {
        
    }

    return result;
}

这里并没有直接创建 surface,而是通过 WindowStateAnimator 创建 WindowSurfaceController 对象,然后从 WindowSurfaceController 对象中把底层 surface handle 复制给 app 端。

WindowSurfaceController 是管理用于 app 绘制的 surface 的,例如,创建与销毁,来看下它是如何创建 surface

java 复制代码
// WindowStateAnimator.java

WindowSurfaceController createSurfaceLocked() {
    final WindowState w = mWin;

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

    // 先重置 WindowState#mHasSurface 为 false
    w.setHasSurface(false);

    ProtoLog.i(WM_DEBUG_ANIM, "createSurface %s: mDrawState=DRAW_PENDING", this);
    
    // 1. 重置窗口的绘制状态
    // 这里把绘制状态 WindowStateAnimator#mDrawState 重置为 DRAW_PENDING
    // 同时也清理了 ActivityRecord 的 all drawn 状态
    resetDrawState();

    mService.makeWindowFreezingScreenIfNeededLocked(w);

    // 注意这个标志位,surface 被创建时,是隐藏状态
    int flags = SurfaceControl.HIDDEN;
    final WindowManager.LayoutParams attrs = w.mAttrs;

    if (w.isSecureLocked()) {
        flags |= SurfaceControl.SECURE;
    }

    // ...

    // Set up surface control with initial size.
    try {

        // This can be removed once we move all Buffer Layers to use BLAST.
        final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0;
        final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;

        // 2. 创建 WindowSurfaceController
        mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
                flags, this, attrs.type);
        mSurfaceController.setColorSpaceAgnostic(w.getPendingTransaction(),
                (attrs.privateFlags & LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);

        // 3. 现在窗口有绘制的 surface 了,因此把 WindowState#mHasSurface 设置为 true
        w.setHasSurface(true);
        
        // ...

        ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
                    "  CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x / %s",
                    mSurfaceController, mSession.mSurfaceSession, mSession.mPid, attrs.format,
                    flags, this);
    } catch (Exception e) {}

    
    // 4. 当前用于 app 绘制的surface处于隐藏状态,因此 mLastHidden 为 true
    mLastHidden = true;

    if (DEBUG) Slog.v(TAG, "Created surface " + this);
    return mSurfaceController;
}

在这一步中,除了创建 WindowSurfaceController 以外,有很多状态需要注意

  1. WindowState#mDrawState 代表窗口的绘制状态,在这里被重置为 DRAW_PENDING,表示窗口即将被绘制。
  2. WindowState#mHasSurface 代表窗口是否有了 app 绘制的 surface,它在 WindowSurfaceController 创建以后,被设置为 true,因为 WindowSurfaceController 会自动创建 app 绘制的 surface。
  3. WindowStateAnimator#mLastHidden 代表 app 绘制的 surface 是否处于隐藏状态。由于在创建 WindowSurfaceController 时,使用了 SurfaceControl.HIDDEN 标志位,它会导致 surface 在创建后是隐藏状态,因此 WindowStateAnimator#mLastHidden 会被设置为 true。这个状态变量很关键,在窗口大刷新的时候,它会决定是否 show app 绘制的 surface。

WindowSurfaceController 的构造函数中,会创建用于 app 绘制的 surface,如下

java 复制代码
// WindowSurfaceController.java

WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator,
        int windowType) {
    // ...

    final SurfaceControl.Builder b = win.makeSurface()
            // 注意这里,app 绘制的 surface,最终的 parent 是 WindowState surface
            .setParent(win.getSurfaceControl())
            .setName(name)
            .setFormat(format)
            .setFlags(flags)
            .setMetadata(METADATA_WINDOW_TYPE, windowType)
            .setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
            .setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
            .setCallsite("WindowSurfaceController");

    // ...

    mSurfaceControl = b.build();
}

很简单,就是利用传入的参数,创建一个 SurfaceControl。注意,它的 parent 是 WindowState surface。

窗口绘制完成

从 Android S 开始,默认开启了 BLAST 绘制方式。app 的绘制 buffer,可以保存到事务 Transaction 中。WMS 修改窗口的数据,例如,修改 surface 位置,也可以保存到事务 Transaction 中。如果想做到 app 端和 WMS 的同步,那么可以把 app 端的绘制 buffer,以 Transaction 的形式提交到 WMS 端,WMS 再把它合并到自己的 Transaction 中,最后 apply,实现同步更新。

但是,并不是每一次 app 端绘制完成,都会把绘制 buffer 提交给 WMS 端。而对于首次绘制,app 端是需要提交绘制 buffer 的。恰好,relaunch activity 会重新创建 ViewRootImpl,并完成绘制,这就是首次绘制。此时 app 端会通过 session ,通知 WMS 端绘制完成,并传递带有绘制 buffer 的 Transaction,如下

java 复制代码
// WindowManagerService.java

// postDrawTransaction 带有 app 端绘制 buffer
void finishDrawingWindow(Session session, IWindow client,
        @Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
    if (postDrawTransaction != null) {
        postDrawTransaction.sanitize(Binder.getCallingPid(), Binder.getCallingUid());
    }

    final long origId = Binder.clearCallingIdentity();
    try {
        synchronized (mGlobalLock) {
            // 找到对应的 WindowState
            WindowState win = windowForClientLocked(session, client, false);
            
            ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s",
                    win, (win != null ? win.mWinAnimator.drawStateToString() : "null"));
            // 1. 由 WindowState 处理 finish drawing
            if (win != null && win.finishDrawing(postDrawTransaction, seqId)) {
                if (win.hasWallpaper()) {
                    win.getDisplayContent().pendingLayoutChanges |=
                            WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                }
                
                // 2. 标记 DisplayContent 需要 layout,并且执行大刷新
                // 更新 DisplayContent#mLayoutNeeded 为 true
                win.setDisplayLayoutNeeded();
                mWindowPlacerLocked.requestTraversal();
            }
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

WMS 处理绘制完成的窗口

  1. 通知对应的 WindowState 处理 finish drawing,并且还传入了带有 app 绘制 buffer 的 postDrawTransaction。 WindowState 主要更新了绘制状态和同步状态,并且还合并了 postDrawTransaction。参考【WindowState 处理 finish drawing
  2. 如果 WindowState 处理 finish drawing 返回 true,那么表示需要执行窗口大刷新。这里还把 DisplayContent#mLayoutNeeded 设置 true,表示大刷新时,需要对 DisplayContent 下的窗口执行 layout。那么,为何要执行大刷新? 因为,用于 app 绘制的 surface 还没 show 出来呢,动画也需要大刷新触发。参考【窗口大刷新

WindowState 处理 finish drawing

java 复制代码
// WindowState.java

boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction, int syncSeqId) {
    if (mOrientationChangeRedrawRequestTime > 0) {
        
    } else if (mActivityRecord != null && mActivityRecord.mRelaunchStartTime != 0
            && mActivityRecord.findMainWindow(false /* includeStartingApp */) == this) {
        final long duration =
                SystemClock.elapsedRealtime() - mActivityRecord.mRelaunchStartTime;
        
        // 这个 log 展示重绘所用的时间,可以作为屏幕旋转动画性能优化的一个指标
        Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
        mActivityRecord.finishOrAbortReplacingWindow();
    }
    
    // ...

    // 在 postDrawTransaction 中,执行 DrawHandler
    // 此时不会执行这种情况
    final boolean hasSyncHandlers = executeDrawHandlers(postDrawTransaction, syncSeqId);

    boolean skipLayout = false;
    boolean layoutNeeded = false;
    // Control the timing to switch the appearance of window with different rotations.
    final AsyncRotationController asyncRotationController =
            mDisplayContent.getAsyncRotationController();
    if (asyncRotationController != null
            && asyncRotationController.handleFinishDrawing(this, postDrawTransaction)) {
        // 处理异步旋转窗口...
    } else if (syncActive) {
        // 处理用序号进行同步的情况
    } 
    // 1. 如果处于同步状态,或者带有 DrawHandler
    else if (useBLASTSync()) {
        // 更新同步状态为 SYNC_STATE_READY
        layoutNeeded = onSyncFinishedDrawing();
    }

    // 2. 更新绘制状态,以及合并 app 端绘制 buffer
    layoutNeeded |= mWinAnimator.finishDrawingLocked(postDrawTransaction);
    
    // 返回值决定了是否发起大刷新,从目前的执行流程看,返回 true
    // We always want to force a traversal after a finish draw for blast sync.
    return !skipLayout && (hasSyncHandlers || layoutNeeded);
}

MainActivity 的窗口完成绘制,由于它对应的 WindowState 处于同步状态,因此 WindowState 处理 finish drawing 过程如下

  1. WindowState 自己调用 onSyncFinishedDrawing() 把同步状态 mSyncState 切换到 SYNC_STATE_READY
  2. 通知 WindowStateAnimator 处理 finish drawing。WindowStateAnimator 会把绘制状态 WindowStateAnimator#mDrawState 更新为 COMMIT_DRAW_PENDING ,并且用 WindowState 的 sync transaction 合并 app 端提交的 postDrawTransaction

大致看下 WindowStateAnimator 处理 finish drawing 过程,如下

java 复制代码
// WindowStateAnimator.java

boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction) {
    final boolean startingWindow =
            mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
    if (startingWindow) {

    }

    boolean layoutNeeded = false;

    if (mDrawState == DRAW_PENDING) {
        ProtoLog.v(WM_DEBUG_DRAW,
                "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s", mWin,
                mSurfaceController);
        if (startingWindow) {
        }
        
        // 切换绘制状态到 COMMIT_DRAW_PENDING
        mDrawState = COMMIT_DRAW_PENDING;
        layoutNeeded = true;
    }

    if (postDrawTransaction != null) {
        // app 绘制的 buffer,merge 到 WindowState 的 sync transaction
        mWin.getSyncTransaction().merge(postDrawTransaction);
        layoutNeeded = true;
    }

    // 返回 true
    return layoutNeeded;
}

窗口大刷新

由于窗口大刷新的调用链太长了,因此本文只展示关键节点的代码,并且也省略与旋转动画无关的代码。

窗口大刷新的本质,其实就是对 DisplayContent 下的窗口进行处理,例如,layout,show surface,等等。因此,大刷新的代码,就从 RootWindowContainer 开始看起

java 复制代码
// RootWindowContainer.java

void performSurfacePlacementNoTrace() {
    // ...
    
    mWmService.openSurfaceTransaction();
    try {
        // 1. 对 RWC 下的 DisplayContent 执行 applySurfaceChangesTransaction()
        applySurfaceChangesTransaction();
    } catch (RuntimeException e) {
        Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
    } finally {
        mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
        // ...
    }

    // ...
    
    // 2.检测 shell transition 动画是否已经同步完成
    mWmService.mSyncEngine.onSurfacePlacement();
    
    // ...
}

RootWindowContainer 先让 DisplayContent 处理自己的窗口,然后再根据处理的结果,做一些其他事情,例如触发动画。

先来看下 DisplayContent 是如何处理其下的窗口的

java 复制代码
// DisplayContent.java

void applySurfaceChangesTransaction() {
    final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;

    // ...
    
    // 1. 对窗口进行 layout
    performLayout(true /* initial */, false /* updateInputWindows */);
    
    // ...

    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
    try {
        // 2. 处理窗口的改变
        forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
    } finally {
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
    
    // 3. 为窗口准备 surface
    prepareSurfaces();

    // ...
}

DisplayContent 处理窗口的过程如下

  1. 对所有窗口进行 layout。
  2. 处理窗口改变。窗口的绘制状态改变,就是其中一个改变。前面刚刚把窗口绘制状态切换到 COMMIT_DRAW_PENDING,这里会继续处理窗口绘制状态。参考【处理窗口改变
  3. prepare surface。这里会更新 surface position,show surface,等等。参考【prepare surface

处理窗口改变

java 复制代码
// DisplayContent.java

private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
    // ...

    final WindowStateAnimator winAnimator = w.mWinAnimator;
    if (w.mHasSurface) { // relayout window 时,创建了 surface
        // 提交完成的绘制,这期间会再次更新绘制状态
        final boolean committed = winAnimator.commitFinishDrawingLocked();
        if (isDefaultDisplay && committed) {
            if (w.hasWallpaper()) {

            }
        }
    }

    // ...
};

DisplayContent 是从上大小处理窗口改变,目前 MainActivity 的 WindowState 已经有了用于 app 绘制的 surface,并且已经绘制完成,因此这里会再次更新绘制状态,如下

java 复制代码
// WindowStateAnimator.java

boolean commitFinishDrawingLocked() {
    if (DEBUG_STARTING_WINDOW_VERBOSE &&
            mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
        Slog.i(TAG, "commitFinishDrawingLocked: " + mWin + " cur mDrawState="
                + drawStateToString());
    }
    
    // 此时绘制状态为 COMMIT_DRAW_PENDING
    if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
        return false;
    }
    
    ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s",
            mSurfaceController);
    
    // 1.把窗口绘制状态切换到 READY_TO_SHOW
    mDrawState = READY_TO_SHOW;
    
    boolean result = false;
    final ActivityRecord activity = mWin.mActivityRecord;
    
    // 2. 尝试会把窗口的绘制状态切换到 HAS_DRAWN
    if (activity == null || activity.canShowWindows()
            || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
        result = mWin.performShowLocked();
    }
    return result;
}

由于此时的绘制状态为 COMMIT_DRAW_PENDING,因此绘制状态会可以切换到 READY_TO_SHOW。然后,会把绘制状态切换到 HAS_DRAWN,但是 ActivityRecord 必须能显示窗口,如下

java 复制代码
// ActivityRecord.java

boolean canShowWindows() {
    final boolean drawn = mTransitionController.isShellTransitionsEnabled()
            ? mSyncState != SYNC_STATE_WAITING_FOR_DRAW : allDrawn;
    final boolean animating = mTransitionController.isShellTransitionsEnabled()
            ? mTransitionController.inPlayingTransition(this)
            : isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION);
    return drawn && !(animating && hasNonDefaultColorWindow());
}

private boolean hasNonDefaultColorWindow() {
    return forAllWindows(ws -> ws.mAttrs.getColorMode() != COLOR_MODE_DEFAULT,
            true /* topToBottom */);
}

判断一个 ActivityRecord 能否显示窗口,有三个条件

  1. ActivityRecord 的同步状态不能是 SYNC_STATE_WAITING_FOR_DRAW。从第一篇文章的分析可以知道,ActivityRecord 此时的同步状态为 SYNC_STATE_READY。
  2. ActivityRecord 不能参与到正在执行的 Transition 中。当前,旋转动画的 Transition 动画还没有执行,因此这个条件也是满足的。
  3. ActivityRecord 下所有窗口的属性中,不能指定非默认的颜色模式。一般来说,很少有窗口指定非默认颜色模式。

对于旋转动画来说,这里三个条件都是满足的,那么就执行 WindowState#performDrawLocked() 来切换下一个绘制状态

java 复制代码
// WindowState.java

boolean performShowLocked() {
    // ...

    // 两个条件,决定窗口绘制状态切换到下一个状态 HAS_DRAWN
    // 一个条件是窗口此时的状态必须为 READY_TO_SHOW
    // 另外一个条件是,WindowToken 必须被允许显示其下的窗口 surface
    if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
        return false;
    }

    // ...

    // Force the show in the next prepareSurfaceLocked() call.
    // 注意,这里强制 mWinAnimator.mLastAlpha 设置为 -1,是为了接下来 prepareSurface()
    // 中的 show surface 做准备
    mWinAnimator.mLastAlpha = -1;
    
    ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
    
    // 窗口状态切换到 HAS_DRAWN
    mWinAnimator.mDrawState = HAS_DRAWN;
    
    mWmService.scheduleAnimationLocked();

    // relayout window 创建 surface 后,会把 mHidden 设置为 true
    // mHidden 应该代表的是窗口是否绘制完成,true代表没有绘制完成,false代表绘制完成
    if (mHidden) {
        // 设置为 false,表示窗口绘制完成
        mHidden = false;
        
        // ...
    }

    return true;
}

在这一步中,窗口的绘制状态,会切换到 HAS_DRAWN。但是,有两个条件,一个是窗口当前的绘制状态必须为 READY_TO_SHOW,这是满足的,另外一个条件如下

java 复制代码
// WindowState.java

boolean isReadyForDisplay() {
    // AppTransition 已经废弃
    if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()) {
        return false;
    }
    
    // 这里判断了三个条件
    // 如果当前窗口是子窗口,那么父窗口(不是指的WindowToken,而是WindowState)不能是隐藏状态
    // 当前窗口可见性为 VISIBLE
    // 当前窗口所属的 WindowToken 需要允许其下的窗口可见
    final boolean parentAndClientVisible = !isParentWindowHidden()
            && mViewVisibility == View.VISIBLE && mToken.isVisible();
    return mHasSurface && isVisibleByPolicy() && !mDestroying
            && (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));
}

boolean isParentWindowHidden() {
    // 注意,这里获取的 parent 是 WindowState,不是 WindowToken
    // 因此,这里的意思是,如果当前窗口是子窗口,那么父窗口不能是隐藏
    final WindowState parent = getParentWindow();
    return parent != null && parent.mHidden;
}

这里有三个条件,对于旋转动画来说,前两个条件是满足的。对于第三个条件,其实也是满足的,对于 Activity 的 WindowState 来说,它的 WindowToken 就是 ActivityRecord,看下它的 isVisible()

java 复制代码
// ActivityRecord.java

private boolean mVisible;        // Should this token's windows be visible?

boolean isVisible() {
    // If the activity isn't hidden then it is considered visible and there is no need to check
    // its children windows to see if they are visible.
    return mVisible;
}

ActivityRecord#mVisible 从注释看,表示 token 其下的窗口是否应该可见。它是在 shell transition 提交可见性的过程中设置的。注意,旋转动画不会更新它,它沿用上一次 shell transition 动画更新的状态。

窗口状态切换流程

窗口绘制状态 WindowStateAnimator#mDrawState 切换流程

  1. relayout window 时,调用 WindowStateAnimator#createSurfaceLocked() 创建 surface 时,把窗口状态重置为 DRAW_PENDING
  2. 当 app 端绘制完成,通知 WMS 时,在 WindowStateAnimator#finishDrawingLocked() 中,把状态切换到 COMMIT_DRAW_PENDING
  3. 大刷新时,DisplayContent 处理窗口改变时,调用 WindowStateAnimator#commitFinishDrawingLocked() 把状态切换到 READY_TO_SHOW 。然后,继续调用 WindowState#performShowLocked() 把状态切换到 HAS_DRAWN

窗口同步状态 WindowState#mSyncState 切换流程

  1. 可见的 WindowState 的任意 parent 被收集到 SyncGroup 时,会调用 parent 的 prepareSync() ,这导致 WindowState 的同步状态切换到 SYNC_STATE_WAITING_FOR_DRAW
  2. 当 app 重绘完成,通知 WMS 时,在 WindowState#onSyncFinishedDrawing() 中,把同步状态切换到 SYNC_STATE_READY

无论是窗口的绘制状态,还是同步状态,每一次的切换,都必须搞清楚条件是什么。

prepare surface

java 复制代码
// DisplayContent.java

void prepareSurfaces() {
    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
    try {
        // 获取 DisplayContent pending transaction
        final Transaction transaction = getPendingTransaction();
        
        super.prepareSurfaces();

        // 把 DisplayContent pending transaction 合并到 global transaction
        SurfaceControl.mergeToGlobalTransaction(transaction);
    } finally {
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
}

// WindowContainer.java

void prepareSurfaces() {
    mCommittedReparentToAnimationLeash = mSurfaceAnimator.hasLeash();
    for (int i = 0; i < mChildren.size(); i++) {
        mChildren.get(i).prepareSurfaces();
    }
}

在 DisplayContent 的 prepare surface 的过程中,有些 surface 的操作是用 DisplayContent pending transaction 执行的,而有些是用 sync transaction 执行的。pending transcation 会被 merge 到 global transaction,伴随着大刷新 apply,而 sync transcation 是在动画开始前 apply 的。

DisplayContent 是通过基类的函数,实现 prepare surface,就是递归让所有 children 执行 prepare surface。由于 WindowState 是(间接)管理 app 绘制的 surface 的,因此看下 WindowState 的 prepare surface 过程

java 复制代码
// WindowState.java

void prepareSurfaces() {
    mIsDimming = false;
    if (mHasSurface) {
        applyDims();
        
        // 使用 sync transaction 更新 surface position
        updateSurfacePositionNonOrganized();
        
        // 使用 DisplayContent 的 pending transaction 把窗口帧率优先级发送给SF
        updateFrameRateSelectionPriorityIfNeeded();
        
        // 使用 sync transaction 对 surface 进行缩放
        updateScaleIfNeeded();
        
        // 使用 sync transcation 去 show app 绘制的 surface
        mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
    }
    super.prepareSurfaces();
}

根据前面的分析,app 绘制的 surface ,由 WindowStateAnimator 下的 WindowSurfaceController 管理。因此,看来 WindowStateAnimator 的 prepare surface

java 复制代码
// WindowStateAnimator.java

// t 为 WindowState 的 sync transcation
void prepareSurfaceLocked(SurfaceControl.Transaction t) {
    final WindowState w = mWin;
    if (!hasSurface()) {
       
    }
    
    // 1. mShownAlpha 更新为 atrrs.alpha
    computeShownFrameLocked();

    if (!w.isOnScreen()) {
        
    } 
    // 在 relayout window 中创建 surface 时,mLastHidden 设置为 true
    // 把绘制状态切换为 HAS_DRAWN 时,强制把 mLastAlpha 设置为了 -1,就是了为了强制 show surface
    // 这里的意思就是如果 alpha 改变,或者用于app绘制surface的隐藏状态改变,就show surface
    else if (mLastAlpha != mShownAlpha
            || mLastHidden) {
        // 更新最新的 alpha
        mLastAlpha = mShownAlpha;
        
        ProtoLog.i(WM_SHOW_TRANSACTIONS,
                "SURFACE controller=%s alpha=%f HScale=%f, VScale=%f: %s",
                mSurfaceController, mShownAlpha, w.mHScale, w.mVScale, w);

        // 2.使用 sync transaction 设置 surface 的 alpha
        // 把 alpha 设置给 app 绘制的 surface
        boolean prepared =
            mSurfaceController.prepareToShowInTransaction(t, mShownAlpha);

        if (prepared && mDrawState == HAS_DRAWN) { // 注意,绘制状态必须为 HAS_DRAWN
            if (mLastHidden) { // 处于隐藏状态才show
                // 3. show surface
                mSurfaceController.showRobustly(t);
                
                // mLastHidden 重置为 false,代表 app 绘制的 surface 已经显示
                mLastHidden = false;
                
                final DisplayContent displayContent = w.getDisplayContent();
                if (!displayContent.getLastHasContent()) {
                    
                }
            }
        }
    }

    if (w.getOrientationChanging()) {
        
    }
}

这里的主要操作其实就是在 WindowSurfaceController 中,使用 sync transaction 对 app 绘制 surface,设置透明度以及show。

不过,要 show surface,要注意两个条件

  1. 绘制状态必须要为 HAS_DRWAN。根据前面的分析,这个条件是满足的。
  2. mLastHidden 为 true,表示 app 绘制 surface 当前处于隐藏状态。根据前面的分析,在创建 surface 的时候,通过 flag 让其处于隐藏状态,然后 mLastHidden 被设置为 true。

OK,最后展示下 WindowSurfaceController 如何 show app 绘制的 surface 的

java 复制代码
// WindowSurfaceController.java

void showRobustly(SurfaceControl.Transaction t) {
    ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SHOW (performLayout): %s", title);
    if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
            + " during relayout");

    if (mSurfaceShown) {
        return;
    }
    
    // 主要更新 mSurfaceShown 为 true
    setShown(true);
    
    // 通过 sync transaction 去 show surface
    t.show(mSurfaceControl);
    if (mAnimator.mIsWallpaper) {

    }
}

结束

在 WMS 中,要分析一个功能,需要考虑到方方面面。但是,我不可能把所有细节都展示出来。这篇文章,我也是反复斟酌,反复修改,力求能让更多人看懂。但是,说实话,我还是不满意。

出于个人时间和精力的考虑,这篇文章暂时就这样展示出来,后面我还会斟酌细节,如果发现不合理或者不好的地方,我再修改吧! 如果各位有什么疑问,或者建议,也欢迎提出,bye~

相关推荐
拭心11 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王13 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡13 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道14 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库15 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道15 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe15 小时前
Android Hook - 动态加载so库
android
居居飒16 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He19 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗19 小时前
Android笔试面试题AI答之Android基础(1)
android