问题现象

车模卡片是 SurfaceView,当问题出现后滑动 Launcher activity 时 SurfaceView 不跟随移动,如下所示。

surface_detach.mp4

SurfaceView 移动原理

Activity 和 SurfaceView 在 SurfaceFlinger 中都是一个 Layer,SurfaceView 的父是 ViewRootImpl.mBoundsLayer,它的父是 ViewRootImpl。当左右滑动 Launcher 的时候,会把新的位置通过 Transaction 设置给 SurfaceView 对应的 Layer 来改变 SurfaceView 的位置。

原因分析

直接原因

直接原因是一次 Transaction 最后会调用 Layer::doTransaction() 函数,当 mLayerDetached 是 true 时,无法执行任何 Transaction,当然也无法改变位置。

uint32_t Layer::doTransaction(uint32_t flags) {
    if (mLayerDetached) {
        ...
        return flags;
    }
    ...
    State c = getCurrentState();
    ...
    // Commit the transaction
    commitTransaction(c);
    ...
    return flags;
}

mLayerDetached 是在 Layer::detachChildren() 中设置的。

bool Layer::detachChildren() {
    for (const sp<Layer>& child : mCurrentChildren) {
        sp<Client> parentClient = mClientRef.promote();
        sp<Client> client(child->mClientRef.promote());
        if (client != nullptr && parentClient != client) {
            child->mLayerDetached = true;
            child->detachChildren();
            child->removeRemoteSyncPoints();
        }
    }
    return true;
}

detachChildren 的目的

// Detaches all child surfaces (and their children recursively)
// from their SurfaceControl.
// The child SurfaceControls will not throw exceptions or return errors,
// but transactions will have no effect.
// The child surfaces will continue to follow their parent surfaces,
// and remain eligible for rendering, but their relative state will be
// frozen. We use this in the WindowManager, in app shutdown/relaunch
// scenarios, where the app would otherwise clean up its child Surfaces.
// Sometimes the WindowManager needs to extend their lifetime slightly
// in order to perform an exit animation or prevent flicker.
Transaction& detachChildren(const sp<SurfaceControl>& sc);

注释说明,detachChildren() 之后可以继续绘制,但是关联关系(relative state)被冻结了,这里就包含与父的位置关系。在应用关闭或重启时,应用会清理子 Surface,这时 WindowManager 需要延长它们的生命周期来执行退出动画或避免闪烁。

基于注释猜测 detachChildren() 是为了在应用退出时暂时延长 Surface 生命周期,但最终是要销毁的。如果销毁了,下一次显示时会重新创建新 Surface,同时与父建立联系,这样就没有问题了。