Android Framework-WMS-动画-初步认识

昨天又出去面试了,面试官问我 动画这部分你知道么? 我说知道,不就是常见的帧动画,属性动画那堆么... 面试官说我问你WMS里面的window的动画原理.. 我说emmm...,又回家等消息了。

咋现在面试那么难。 开始面向AI学习。(源码是aosp13

日常我们调用WindowManager 添加一个view 带动画,基本如下写法(我还特地查了下

java 复制代码
  val v = View(this)
        mWindowManager = getSystemService(WINDOW_SERVICE) as WindowManager;

//省略...
        val mLayoutParams = WindowManager.LayoutParams();
//省略...
        mLayoutParams.windowAnimations = R.style.window_anim;
        mWindowManager.addView(tv, mLayoutParams)
xml 复制代码
<style name="window_anim">
    <item name="android:windowEnterAnimation">@anim/window_enter</item>
    <item name="android:windowExitAnimation">@anim/window_exit</item>
</style>

我就好奇这动画咋加进去的。

WindowStateperformShowLocked 这里

java 复制代码
mWmService.enableScreenIfNeededLocked();
mWinAnimator.applyEnterAnimationLocked();

mWinAnimator.mLastAlpha = -1;
ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
//这里是HAS_DRAWN
mWinAnimator.mDrawState = HAS_DRAWN;
mWmService.scheduleAnimationLocked();

代码大体流程

js 复制代码
WindowStateAnimator.applyEnterAnimationLocked()
//这里壁纸是无法进入的在该代码外面有一圈判断 if(mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper)
  //1.1
  -->  applyAnimationLocked(transit, true); 
 --> mWin.startAnimation(a); //这里的动画adapter 是LocalAnimationAdapter
 -->WindowState.startAnimation()
   -->WindowContainer.startAnimation()
   --> mSurfaceAnimator.startAnimation(t, anim, hidden, type,animationFinishedCallback, mSurfaceFreezer);
   //1.3
   -->SurfaceAnimator.startAnimation() 
     
java 复制代码
void applyEnterAnimationLocked() {
 //省略....
public static final int TRANSIT_SHOW = 3;
public static final int TRANSIT_ENTER = 1;

    final int transit;
    if (mEnterAnimationPending) {
        mEnterAnimationPending = false;
        transit = WindowManagerPolicy.TRANSIT_ENTER;
    } else {
        transit = WindowManagerPolicy.TRANSIT_SHOW;
    }

//省略
    if (mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper) {
        applyAnimationLocked(transit, true);
    }

    if (mService.mAccessibilityController.hasCallbacks()) {
        mService.mAccessibilityController.onWindowTransition(mWin, transit);
    }
}

也就是说默认传到applyAnimationLocked 里面的 值 不是TRANSIT_SHOW 就是TRANSIT_ENTER 这里我打log 启动Activity的时候, 看了下是TRANSIT_SHOW 然后是 TRANSIT_ENTER

1.1 WindowStateAnimator.applyAnimationLocked()

java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java

boolean applyAnimationLocked(int transit, boolean isEntrance) {
        if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) {
          
            return true;
        }

        final boolean isImeWindow = mWin.mAttrs.type == TYPE_INPUT_METHOD;
        if (isEntrance && isImeWindow) {
            mWin.getDisplayContent().adjustForImeIfNeeded();
            mWin.setDisplayLayoutNeeded();
            mService.mWindowPlacerLocked.requestTraversal();
        }       
        if (mWin.mToken.okToAnimate()) {
          // 1.2DisplayPolicy.selectAnimation 默认返回0了
            int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
            int attr = -1;
            Animation a = null;
            if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
              //不等于0  就去查找对应的动画
                if (anim != DisplayPolicy.ANIMATION_NONE) {
                    a = AnimationUtils.loadAnimation(mContext, anim); 
                }
            } else {
                switch (transit) {
                    //我们是0 就去加载对应的动画
                    case WindowManagerPolicy.TRANSIT_ENTER:
                        attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
                        break;
                    case WindowManagerPolicy.TRANSIT_EXIT:
                        attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
                        break;
                    case WindowManagerPolicy.TRANSIT_SHOW:
                        attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
                        break;
                    case WindowManagerPolicy.TRANSIT_HIDE:
                        attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
                        break;
                }
                if (attr >= 0) {//
                    a = mWin.getDisplayContent().mAppTransition.loadAnimationAttr(
                            mWin.mAttrs, attr, TRANSIT_OLD_NONE);
                }
            }
            
            if (a != null) {//不为null 执行 如果我们不设置动画 那么这里就是null
              //这里属性主要是 layoutParams.windowAnimations
        
                mWin.startAnimation(a);
              
                mAnimationIsEntrance = isEntrance;
            }
        } else if (!isImeWindow) {
            mWin.cancelAnimation();
        }

        if (!isEntrance && isImeWindow) {
            mWin.getDisplayContent().adjustForImeIfNeeded();
        }

        return mWin.isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
    }

稍微总结一下,其实这里就是看我们有没有给window 设置动画,如果有的话就会去我们配置的windowEnterAnimation/windowExitAnimation/windowShowAnimation 等去匹配对应的动画值。

1.2 DisplayPolicy.selectAnimation()

java 复制代码
int selectAnimation(WindowState win, int transit) {
    ProtoLog.i(WM_DEBUG_ANIM, "selectAnimation in %s: transit=%d", win, transit);

    if (transit == TRANSIT_PREVIEW_DONE) {
        if (win.hasAppShownWindows()) {
            if (win.isActivityTypeHome()) {
                // Dismiss the starting window as soon as possible to avoid the crossfade out
                // with old content because home is easier to have different UI states.
                return ANIMATION_NONE;
            }
            ProtoLog.i(WM_DEBUG_ANIM, "**** STARTING EXIT");
            return R.anim.app_starting_exit;
        }
    }

    return ANIMATION_STYLEABLE;
}

这里匹配不到 默认返回 ANIMATION_STYLEABLE 为0 则进行进行匹配。

1.3 WindowState.startAnimation()

java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void startAnimation(Animation anim) {

    // If we are an inset provider, all our animations are driven by the inset client.
    if (mControllableInsetProvider != null) {
        return;
    }

    final DisplayInfo displayInfo = getDisplayInfo();
    anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
            displayInfo.appWidth, displayInfo.appHeight);
    anim.restrictDuration(MAX_ANIMATION_DURATION);
    anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
    final AnimationAdapter adapter = new LocalAnimationAdapter(
            new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
                    0 /* windowCornerRadius */),
            mWmService.mSurfaceAnimationRunner);
    startAnimation(getPendingTransaction(), adapter);
    commitPendingTransaction();
}

将我们查到的动画 ,然后 构建了一个LocalAnimationAdapter 然后调用了 startAnimation 继续调用来到了

1.4 WindowContainer.startAnimation()

java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
        @AnimationType int type,
        @Nullable OnAnimationFinishedCallback animationFinishedCallback,
        @Nullable Runnable animationCancelledCallback,
        @Nullable AnimationAdapter snapshotAnim) {
    ProtoLog.v(WM_DEBUG_ANIM, "Starting animation on %s: type=%d, anim=%s",
            this, type, anim);

    // TODO: This should use isVisible() but because isVisible has a really weird meaning at
    // the moment this doesn't work for all animatable window containers.
    mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
            animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}

1.5 SurfaceAnimator.startAnimation()

java 复制代码
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
        @AnimationType int type,
        @Nullable OnAnimationFinishedCallback animationFinishedCallback,
        @Nullable Runnable animationCancelledCallback,
        @Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
    cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
    mAnimation = anim;
    mAnimationType = type;
    mSurfaceAnimationFinishedCallback = animationFinishedCallback;
    mAnimationCancelledCallback = animationCancelledCallback;
    final SurfaceControl surface = mAnimatable.getSurfaceControl();
   //省略...
    mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
    if (mLeash == null) {
        mLeash = createAnimationLeash(mAnimatable, surface, t, type,
                mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
                0 /* y */, hidden, mService.mTransactionFactory);
        mAnimatable.onAnimationLeashCreated(t, mLeash);
    }
    mAnimatable.onLeashAnimationStarting(t, mLeash);
   //省略...
    mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
     //省略...
     
}

static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
        Transaction t, @AnimationType int type, int width, int height, int x, int y,
        boolean hidden, Supplier<Transaction> transactionFactory) {
  
    final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
            .setParent(animatable.getAnimationLeashParent())
            .setName(surface + " - animation-leash of " + animationTypeToString(type))
         
            .setHidden(hidden)
            .setEffectLayer()
            .setCallsite("SurfaceAnimator.createAnimationLeash");
    final SurfaceControl leash = builder.build();
    t.setWindowCrop(leash, width, height);
    t.setPosition(leash, x, y);
    t.show(leash);
    t.setAlpha(leash, hidden ? 0 : 1);
    t.reparent(surface, leash);
    return leash;
}

这里有个 mAnimatable 是 在
WindowContainer 的构造函数了 将自己传入 构建了 SurfaceAnimator中的 mAnimatable。 构建了一个SurfaceControl , 然后调用了reparent 切换了surface的为leash。 其实也就是把当前容器挂载到了mLeash 下。 然后mAnimation 调用了 startAnimation 使用了mLeash。 并 mInnerAnimationFinishedCallback 当做参数传了出去。

java 复制代码
WindowContainer(WindowManagerService wms) {
    mWmService = wms;
    mTransitionController = mWmService.mAtmService.getTransitionController();
    mPendingTransaction = wms.mTransactionFactory.get();
    mSyncTransaction = wms.mTransactionFactory.get();
    mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
    mSurfaceFreezer = new SurfaceFreezer(this, wms);
    
}

1.6 LocalAnimationAdapter.startAnimation()

java 复制代码
//frameworks/base/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
public void startAnimation(SurfaceControl animationLeash, Transaction t,
        @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
    mAnimator.startAnimation(mSpec, animationLeash, t,
            () -> finishCallback.onAnimationFinished(type, this));
}

1.7 SurfaceAnimationRunner. startAnimation()

java 复制代码
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
        Runnable finishCallback) {
    synchronized (mLock) {
        final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
                finishCallback);
        boolean requiresEdgeExtension = requiresEdgeExtension(a);

        if (requiresEdgeExtension) {
            final ArrayList<SurfaceControl> extensionSurfaces = new ArrayList<>();
            synchronized (mEdgeExtensionLock) {
                mEdgeExtensions.put(animationLeash, extensionSurfaces);
            }

            mPreProcessingAnimations.put(animationLeash, runningAnim);

            t.addTransactionCommittedListener(mEdgeExtensionExecutor, () -> {
                final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec();

                final Transaction edgeExtensionCreationTransaction = new Transaction();
                edgeExtendWindow(animationLeash,
                        animationSpec.getRootTaskBounds(), animationSpec.getAnimation(),
                        edgeExtensionCreationTransaction);

                synchronized (mLock) {
                   
                    if (mPreProcessingAnimations.get(animationLeash) == runningAnim) {

                        synchronized (mEdgeExtensionLock) {
                            if (!mEdgeExtensions.isEmpty()) {
                                edgeExtensionCreationTransaction.apply();
                            }
                        }

                        mPreProcessingAnimations.remove(animationLeash);
                        mPendingAnimations.put(animationLeash, runningAnim);
                        if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                            mChoreographer.postFrameCallback(this::startAnimations);
                        }
                    }
                }
            });
        }

        if (!requiresEdgeExtension) {
            mPendingAnimations.put(animationLeash, runningAnim);
            if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                mChoreographer.postFrameCallback(this::startAnimations);
            }

      
            applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
        }
    }
}

这部分代码我结合AI 总结一下, 提交事务,然后下一帧的时候 会回调 startAnimations, 会进入 SurfaceAnimationRunner.startAnimationLocked()执行动画的代码了。

小总结一下

总结了一下, 如果我们给window 设置了动画,他会根据进入的状态,去查找是否设置了动画,如果设置了动画,就会去加载对应的动画,然后构建一个SurfaceControl 也就是mLeash, 然后将当前容器挂载到mLeash上,去执行动画。 为什么要构建mLeash ,我查了一下,主要图省事,因为如果挂载在真的parent上去执行, 如果有其他child 都会收到影响,但是单独构建一个SurfaceControl 等动画结束,再挂回去就行了。在12开始有的mLeash

还是看一下执行完动画 SurfaceAnimationRunner.startAnimationLocked()

java 复制代码
private void startAnimationLocked(RunningAnimation a) {
    final ValueAnimator anim = mAnimatorFactory.makeAnimator();

    // Animation length is already expected to be scaled.
    anim.overrideDurationScale(1.0f);
    anim.setDuration(a.mAnimSpec.getDuration());
    anim.addUpdateListener(animation -> {
        synchronized (mCancelLock) {
            if (!a.mCancelled) {
                final long duration = anim.getDuration();
                long currentPlayTime = anim.getCurrentPlayTime();
                if (currentPlayTime > duration) {
                    currentPlayTime = duration;
                }
                applyTransformation(a, mFrameTransaction, currentPlayTime);
            }
        }

        // Transaction will be applied in the commit phase.
        scheduleApplyTransaction();
    });

    anim.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            synchronized (mCancelLock) {
                if (!a.mCancelled) {

                    mFrameTransaction.setAlpha(a.mLeash, 1);
                }
            }
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            synchronized (mLock) {
                mRunningAnimations.remove(a.mLeash);
                synchronized (mCancelLock) {
                    if (!a.mCancelled) {
                        mAnimationThreadHandler.post(a.mFinishCallback);
                    }
                }
            }
        }
    });
    a.mAnim = anim;
    mRunningAnimations.put(a.mLeash, a);

    anim.start();
    if (a.mAnimSpec.canSkipFirstFrame()) {
    
        anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
    }

    anim.doAnimationFrame(mChoreographer.getFrameTime());
}

onAnimationEnd 中会 回调 a.mFinishCallback 其实最终会回到 SurfaceAnimator.mInnerAnimationFinishedCallback,因为是一路传递过来的。

java 复制代码
private OnAnimationFinishedCallback getFinishedCallback(
        @Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback) {
    return (type, anim) -> {
        synchronized (mService.mGlobalLock) {
            final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
            if (target != null) {
                target.mInnerAnimationFinishedCallback.onAnimationFinished(type, anim);
                return;
            }

            if (anim != mAnimation) {
                return;
            }
            final Runnable resetAndInvokeFinish = () -> {
             //省略
                final OnAnimationFinishedCallback animationFinishCallback =
                        mSurfaceAnimationFinishedCallback;
                reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);
                if (staticAnimationFinishedCallback != null) {
                    staticAnimationFinishedCallback.onAnimationFinished(type, anim);
                }
                if (animationFinishCallback != null) {
                    animationFinishCallback.onAnimationFinished(type, anim);
                }
            };
         
            if (!(mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)
                    || anim.shouldDeferAnimationFinish(resetAndInvokeFinish))) {
                resetAndInvokeFinish.run();
            }
            mAnimationFinished = true;
        }
    };
}

private void reset(Transaction t, boolean destroyLeash) {
    mService.mAnimationTransferMap.remove(mAnimation);
    mAnimation = null;
    mSurfaceAnimationFinishedCallback = null;
    mAnimationType = ANIMATION_TYPE_NONE;
    final SurfaceFreezer.Snapshot snapshot = mSnapshot;
    mSnapshot = null;
    if (snapshot != null) {
        // Reset the mSnapshot reference before calling the callback to prevent circular reset.
        snapshot.cancelAnimation(t, !destroyLeash);
    }
    if (mLeash == null) {
        return;
    }
    SurfaceControl leash = mLeash;
    mLeash = null;
    final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
    mAnimationFinished = false;
    if (scheduleAnim) {
        mService.scheduleAnimationLocked();
    }
}

static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash,
        boolean destroy) {
    boolean scheduleAnim = false;
    final SurfaceControl surface = animatable.getSurfaceControl();
    //找parent
    final SurfaceControl parent = animatable.getParentSurfaceControl();
    final SurfaceControl curAnimationLeash = animatable.getAnimationLeash();


    final boolean reparent = surface != null && (curAnimationLeash == null
            || curAnimationLeash.equals(leash));
    if (reparent) {
        
      //省略...
        if (surface.isValid() && parent != null && parent.isValid()) {
            t.reparent(surface, parent);
            scheduleAnim = true;
        }
    }
    if (destroy) {
        t.remove(leash);
        scheduleAnim = true;
    }

    if (reparent) {
  
        animatable.onAnimationLeashLost(t);
        scheduleAnim = true;
    }
    return scheduleAnim;
}

这里主要是动画结束回调,然后 调用到了reset(),将又调用了t.reparent(surface, parent); 又把surface挂回去了。然后删除了mLeash

简单总结,如果我们写了动画,系统会根据状态去匹配,然后构建mLeash ,切换 parent 挂载到mLeash去执行动画,结束会删除 mLeash ,然后将原有的window 挂回原来的parent

相关推荐
Kapaseker29 分钟前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴1 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android