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

相关推荐
用户2018792831672 小时前
Try-Catch-Finally中的Return执行顺序:一个外卖配送的故事
android
wj0718421542 小时前
Android 内存优化 第2篇
android
用户2018792831672 小时前
浅析Hanlder消息队列处理各种消息的流程
android
用户2018792831672 小时前
浅析Hanlder处理延时消息的流程
android
用户092 小时前
Android面试基础篇(一):基础架构与核心组件深度剖析
android·面试·kotlin
wow_DG4 小时前
【MySQL✨】MySQL 入门之旅 · 第十篇:数据库备份与恢复
android·数据库·mysql
00后程序员张4 小时前
iOS 26 系统流畅度深度剖析,Liquid Glass 视效与界面滑动的实际测评
android·macos·ios·小程序·uni-app·cocoa·iphone
草字5 小时前
Android studio 查看apk的包名,查看包名
android·ide·android studio
、BeYourself5 小时前
Android Studio 详细安装与配置指南
android