第一帧

第二帧

第三帧

录屏后,逐帧分析,上面为问题发生时的前后三帧。

第一帧是正常的,第二帧变黑,发现黑帧也不是全黑,底部导航栏和顶部状态栏也可以看见,对比第一帧,分屏两边APP界面和中间divider消失了,第三帧恢复回来后,两边APP界面回来了,中间还是消失的divider。

目前仅从视频分析,无法得知黑色帧的原因,需要进一步抓trace来分析。

从trace上看,黑色的这一帧layer组成,divider和app的activity的layer都消失了,所以露出了黑色底图,而在下一帧的layer,app的activity layer回来了,但是divider消失了,所以黑色帧应该是某个绘制控制进程设置了分屏的surface hide。

从trace上看,在两帧之间,发送了更新事务的有两个进程,分别是shortcut和systemui,进一步分析能够控制divider的进程只有分屏自己,所以应该是systemUI进程。

查看systemUI分屏控制代码 StageCoordinator.java

    private void onStageVisibilityChanged(StageListenerImpl stageListener) {
        final boolean sideStageVisible = mSideStageListener.mVisible;
        final boolean mainStageVisible = mMainStageListener.mVisible;
        // Divider is only visible if both the main stage and side stages are visible
        setDividerVisibility(isSplitScreenVisible());

        if (mExitSplitScreenOnHide && !mainStageVisible && !sideStageVisible) {
            // Exit split-screen if both stage are not visible.
            // TODO: This is only a temporary request from UX and is likely to be removed soon...
            exitSplitScreen();
        }

        if (mainStageVisible) {
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            if (sideStageVisible) {
                // The main stage configuration should to follow split layout when side stage is
                // visible.
                mMainStage.updateConfiguration(
                        WINDOWING_MODE_MULTI_WINDOW, getMainStageBounds(), wct);
            } else {
                // We want the main stage configuration to be fullscreen when the side stage isn't
                // visible.
                mMainStage.updateConfiguration(WINDOWING_MODE_FULLSCREEN, null, wct);
            }
            // TODO: Change to `mSyncQueue.queue(wct)` once BLAST is stable.
            mTaskOrganizer.applyTransaction(wct);
        }

        mSyncQueue.runInSync(t -> {
            final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
            final SurfaceControl sideStageLeash = mSideStage.mRootLeash;
            final SurfaceControl mainStageLeash = mMainStage.mRootLeash;

            if (dividerLeash != null) {
                if (mDividerVisible) {
                    t.show(dividerLeash)
                            .setLayer(dividerLeash, Integer.MAX_VALUE)
                            .setPosition(dividerLeash,
                                    mSplitLayout.getDividerBounds().left,
                                    mSplitLayout.getDividerBounds().top);
                } else {
                    t.hide(dividerLeash);
                }
            }

            if (sideStageVisible) {
                final Rect sideStageBounds = getSideStageBounds();
                t.show(sideStageLeash)
                        .setPosition(sideStageLeash,
                                sideStageBounds.left, sideStageBounds.top)
                        .setWindowCrop(sideStageLeash,
                                sideStageBounds.width(), sideStageBounds.height());
            } else {
                t.hide(sideStageLeash);
            }

            if (mainStageVisible) {
                final Rect mainStageBounds = getMainStageBounds();
                t.show(mainStageLeash);
                if (sideStageVisible) {
                    t.setPosition(mainStageLeash, mainStageBounds.left, mainStageBounds.top)
                            .setWindowCrop(mainStageLeash,
                                    mainStageBounds.width(), mainStageBounds.height());
                } else {
                    // Clear window crop and position if side stage isn't visible.
                    t.setPosition(mainStageLeash, 0, 0)
                            .setWindowCrop(mainStageLeash, null);
                }
            } else {
                t.hide(mainStageLeash);
            }
        });
    }

通过代码分析,可以看到,当TaskOrganizer通知StageCoordinator main和side stage的visible为false时,StageCoordinator就会将divider和main以及side的surface设置成false。通过调试确认了黑色帧就是在这里产生的,注释掉hide后,从分屏退到桌面正常。

但是这里有两个问题:

1.收到TaskOrganizer通知stage visable为false时hide应该是正常的,为什么hide会导致黑色帧呢?

2.为什么StageCoordinator  hide之后第二帧就恢复了stage,但是没有恢复divider,如果不是StageCoordinator,那么是谁恢复的?

第一个问题:

收到TaskOrganizer通知stage visable为false时hide应该是正常的,为什么hide会导致黑色帧呢?

分屏退出到桌面的过程应该是桌面启动到前台后,分屏就退出到后台消失,这里已经收到了main和side stage的task不可见,那么说明桌面已经启动了,就可以正常交接了,但是hide后,发现底下却没有任何图层,在trace中继续向后寻找几帧后,找到了桌面layer,说明桌面的启动速度要低于ATMS的预期。这点需要进一步到ATMS中查看相关代码。

第二个问题:

为什么StageCoordinator  hide之后第二帧就恢复了stage,但是没有恢复divider,如果不是StageCoordinator,那么是谁恢复的?

在StageCoordinator中没有看到任何地方会恢复,但trace中可以看到第二帧到第三帧是由WMS提交的事务,再进一步追踪,WMS是由shortcut提交的请求触发的,结合退出分屏时,有过渡动画,猜测是因为shortcut控制过渡动画时,将stage的leash surface的可见属性再此设置成了true。由于shortcut拿到的动画target没有divider,所以divider是隐藏的。

所以这里应该存在一个冲突的问题,分屏模块本身不知道动画存在,所以在收到ATMS的TaskOrganizer可见为false就直接退出了,但是动画模块正在绘制,还控制着leash surface,又设置了可见,而这时launcher并没有启动完成,所以闪黑帧根本原因是WMS和ATMS在做动画过渡时,没有考虑分屏和launcher的状态同步导致。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐