设计之妙,理解Android动画流程

本文基于Android 12进行学习研究,参考《深入理解Android内核源码》思路学习总结,如有理解不对,望各位朋友指正,另外可能留存一些疑问,留后续再探索。输出只是为了分享和提升自己。

动画初始化

按照窗口管理策略类中的定义,动画应该被分为四种类型。

  • TRANSIT_ENTER:窗口进入。
  • TRANSIT_EXIT:窗口移除。
  • TRANSIT_SHOW:窗口可见。
  • TRANSIT_HIDE:窗口隐藏。

TRANSIT_PREVIEW_DONE其实不算是动画,而是一个标志启动窗口完成的标志位。在WMSrelayoutWindow函数,会根据窗口状态判断是否要执行动画。下面代码则是判断动画的起始点。

java 复制代码
//仅有在窗口在可见状态下且是一个启动窗口类型或者关联的appToken不隐藏时才会进行重新布局
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
        (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                || win.mActivityRecord.isClientVisible());
//当前不需要重新布局、也已经有Surface,且不是在执行退出动画,则需要考虑是否执行动画
if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
    result |= RELAYOUT_RES_SURFACE_CHANGED;
    if (!win.mWillReplaceWindow) {
		
        ......
        //尝试开始或者退出动画    
        focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange);
    }
}

tryStartExitingAnimation调用了WindowStateAnimator类型对象winAnimatorapplyAnimationLocked函数执行动画。

java 复制代码
private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator,boolean focusMayChange) {
    int transit = WindowManagerPolicy.TRANSIT_EXIT;
    //属性类型是应用窗口
    if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
      //启动窗口完成
        transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
    }
//窗口处于动画变换
     if (win.inTransition()) {
        focusMayChange = true;
        win.mAnimatingExit = true;
    } else if (win.isWinVisibleLw() && win.mDisplayContent.okToAnimate()) {
        String reason = null;
        //执行动画,如果未执行动画或动画失败,则释放相关资源
        if (winAnimator.applyAnimationLocked(transit, false)) {
            reason = "applyAnimation";
            focusMayChange = true;
            win.mAnimatingExit = true;
        } else if (
                win.isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)
                || win.isAnimating(PARENTS | TRANSITION,ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)|| (win.inTransition()
            reason = "animating";
            win.mAnimatingExit = true;
        } else if (win.mDisplayContent.mWallpaperController.isWallpaperTarget(win)
                && win.mAttrs.type != TYPE_NOTIFICATION_SHADE) {
            reason = "isWallpaperTarget";
            win.mAnimatingExit = true;
        }
    .....
        }
    }
    //资源释放
    if (!win.mAnimatingExit) {
        boolean stopped = win.mActivityRecord == null || win.mActivityRecord.mAppStopped;
        win.mDestroying = true;
        win.destroySurface(false, stopped);
    }
    if (mAccessibilityController.hasCallbacks()) {
        mAccessibilityController.onWindowTransition(win, transit);
    }

    return focusMayChange;
}

applyAnimationLocked函数选择合适的动画类型,并设置给WindowState,这里也是 判断是否有设置动画的地方。如果设置了动画,调用WindowStatestartAnimation函数。

java 复制代码
boolean applyAnimationLocked(int transit, boolean isEntrance) {
  //避免加载相同动画两次
    if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) {
        return true;
    }

    if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
        mWin.getDisplayContent().adjustForImeIfNeeded();
        if (isEntrance) {
            mWin.setDisplayLayoutNeeded();
            mService.mWindowPlacerLocked.requestTraversal();
        }
    }

    if (mWin.mControllableInsetProvider != null) {
        // All our animations should be driven by the insets control target.
        return false;
    }

    // 如果显示处于 frozen状态,则不执行动画
    if (mWin.mToken.okToAnimate()) {
        int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
        int attr = -1;
        Animation a = null;
        //anim不等于ANIMATION_STYLEABLE,表示给窗口指定了动画
        if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {//anim不等于0,表示指定了动画
            if (anim != DisplayPolicy.ANIMATION_NONE) {
                //加载动画资源
                a = AnimationUtils.loadAnimation(mContext, anim);
            }
        } else {
          //根据动画类型,选择默认的资源文件
            switch (transit) {
                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) {
            mWin.startAnimation(a);//获取到动画资源,开始执行动画Animation对象
            mAnimationIsEntrance = isEntrance;
        }
    } else {
        mWin.cancelAnimation();
    }

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

WindowState在WMS表示一个窗口对象,startAnimation函数对动画anim进行进行初始化,封装成AnimationAdpater类型的adapter对象,并调用父类WindowContainerstartAnimation函数。WindowContainer定义了窗口和窗口层级之间一些通用接口。WindowContainer用来描述动画和联系相关联的组件,以及执行动画的。

注意到这里adpter实际类型是LocalAnimationAdapter,表示该动画不需要持有WindowManager 锁。重点是其构造函数的入参WindowAnimationSpecSurfaceAnimationRunner对象,是后续监听VSYNC进行属性计算的地方。

java 复制代码
void startAnimation(Animation anim) {
    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());
	//SurfaceAnimationRunner类型的 mWmService.mSurfaceAnimationRunner
    final AnimationAdapter adapter = new LocalAnimationAdapter(
            new WindowAnimationSpec(anim, mSurfacePosition, false,
                    0),
            mWmService.mSurfaceAnimationRunner);
    //getPendingTransaction分析
    startAnimation(getPengetPendingTransaction分析dingTransaction(), adapter);
    commitPendingTransaction();
}

WindowContainer.startAnimation函数又调用了SurfaceAnimator.startAnimationSurfaceAnimator主要是将一组子Surface通过约束重置为新的Surface,称为Leach,然后交由AnimationAdapter执行动画。

java 复制代码
// mWinAnimator.mLastHidden, ANIMATION_TYPE_WINDOW_ANIMATION
//animationFinishedCallback、animationCancelledCallback、snapshotAnim此时为null
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
        @AnimationType int type,
        @Nullable OnAnimationFinishedCallback animationFinishedCallback,
        @Nullable Runnable animationCancelledCallback,
        @Nullable AnimationAdapter snapshotAnim) {
    mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
            animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}

startAnimation建立动画链成功之后,就会调用入参anim执行动画。hidden表示WindowContainer当前持有Surface是否可见。按前面分析,此时startAnimation只有前面四个参数有值。三种动画:Surface、adapter、snapshot。

java 复制代码
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
        @AnimationType int type,
        @Nullable OnAnimationFinishedCallback animationFinishedCallback,
        @Nullable Runnable animationCancelledCallback,
        @Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
   //强制取消与t相同类型的动画,并重新执行
   cancelAnimation(t, true /* restarting */, true /* forwardCancel */);

    mAnimation = anim;
    mAnimationType = type;
    mSurfaceAnimationFinishedCallback = animationFinishedCallback;
    mAnimationCancelledCallback = animationCancelledCallback;

    final SurfaceControl surface = mAnimatable.getSurfaceControl();
    if (surface == null) {
        cancelAnimation();
        return;
    }
    //freezer==null
    mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
    if (mLeash == null) {
      //创建动画链(SurfaceController),同时也会设置给t
        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);

    if (mAnimationStartDelayed) {
        return;
    }
    //此时执行AnimationAdapter对象mAnimation动画,mInnerAnimationFinishedCallback是WC的监听
    mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
   //开始snapshot动画,此时snapshotAnim==null
   if (snapshotAnim != null) {
        mSnapshot = freezer.takeSnapshotForAnimation();
        if (mSnapshot == null) {
            return;
        }
        mSnapshot.startAnimation(t, snapshotAnim, type);
    }
}

按前面的分析,这里mAnimation的实际类型是LocalAnimationAdapter。其startAnimation又调用了SurfaceAnimationRunner对象的startAnimation函数。

先是将动画相关内容封装成RunningAnimation对象,并存放到mPendingAnimations中。

然后向编舞者Choreographer注册了VSYNC监听,这样当信号来临时,会调用startAnimations函数。

java 复制代码
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
        Runnable finishCallback) {
    synchronized (mLock) {
        final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
                finishCallback);
        //这样帧信号来临时,会执行该Map中所有动画
        mPendingAnimations.put(animationLeash, runningAnim);
        //如果当前动画部延迟执行,则立即向Choreographer注册垂直信号监听
        //这样垂直信号来临时,执行startAnimations,存在一定延迟
        if (!mAnimationStartDeferred) {
            mChoreographer.postFrameCallback(this::startAnimations);
        }
        //一些动画需要初始并应用一些变换
        applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
    }
}

SurfaceAnimationRunnerstartAnimations函数。

java 复制代码
private void startAnimations(long frameTimeNanos) {
    synchronized (mLock) {
        startPendingAnimationsLocked();
    }
    mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
}

遍历mPendingAnimations中所有等待帧信号来临的RunningAnimation对象,调用startAnimationLocked执行它们。

java 复制代码
private void startPendingAnimationsLocked() {
    for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
        startAnimationLocked(mPendingAnimations.valueAt(i));
    }
    mPendingAnimations.clear();
}

SurfaceAnimatorRunner对象,startAnimationLocked主要为每个RunningAnimation动画创建ValueAnimator对象并设置相关参数,并在AnimatorUpdateListener回调中计算动画应用对象的相关属性。

java 复制代码
@GuardedBy("mLock")
private void startAnimationLocked(RunningAnimation a) {
    final ValueAnimator anim = mAnimatorFactory.makeAnimator();
    //设置动画相关参数
    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);
            }
        }

        scheduleApplyTransaction();
    });
	//anim开始后,会回调此处。
    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);
	//调用ValueAnimator的start函数。
    anim.start();
    if (a.mAnimSpec.canSkipFirstFrame()) {
        anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
    }
	//手动触发一帧动画属性
    anim.doAnimationFrame(mChoreographer.getFrameTime());
}

ValueAnimator.start=>start(false),该函数主要ValueAnimator对象对一些变量进行初始化。其中调用了addAnimationCallback(0)AnimationHandler注册帧信号的监听。

java 复制代码
private void start(boolean playBackwards) {
    if (Looper.myLooper() == null) {
        throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    }
    mReversing = playBackwards;//此时false,表示为正向动画
    mSelfPulse = !mSuppressSelfPulseRequested;
    //判断是否快进到某个时间点
    if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
        if (mRepeatCount == INFINITE) {
            // Calculate the fraction of the current iteration.
            float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
            mSeekFraction = 1 - fraction;
        } else {
            mSeekFraction = 1 + mRepeatCount - mSeekFraction;
        }
    }
    mStarted = true;
    mPaused = false;
    mRunning = false;
    mAnimationEndRequested = false;

    mLastFrameTime = -1;
    mFirstFrameTime = -1;
    mStartTime = -1;
    addAnimationCallback(0);

    if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
        //内部初始化动画对象所应用的属性,和回调开始监听
        startAnimation();
        //初始化mStartTime
        if (mSeekFraction == -1) {
            setCurrentPlayTime(0);
        } else {
            setCurrentFraction(mSeekFraction);
        }
    }
}

AnimationHandler在线程中静态单例的,在默认情况下,向编舞者Choreographer注册并接收其帧信号(垂直信号)回调, 向所有的动画ValueAnimator对象发送刷新帧的时间,这样它们在同一个线程以相同的时间执行各自窗口属性值的计算,达到视觉动画效果。AnimationHandler也为我们提供 设置类似编舞者提供周期信号的 接口,方便测试和特定功能开发。

java 复制代码
private void addAnimationCallback(long delay) {
    if (!mSelfPulse) {
        return;
    }
    getAnimationHandler().addAnimationFrameCallback(this, delay);
}

public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
	//注册垂直信号监听
    if (mAnimationCallbacks.size() == 0) {
        getProvider().postFrameCallback(mFrameCallback);
    }
    //避免重复添加
    if (!mAnimationCallbacks.contains(callback)) {
        mAnimationCallbacks.add(callback);
    }
	//延时执行
    if (delay > 0) {
        mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
    }
}
//这样垂直信号来临,会调用doFrame函数
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());
        if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);
        }
    }
};

动画触发

AnimationHandler默认情况会向编舞者Choreographer注册垂直信号(VSYNC)监听,这样当垂直信号来临时,就回调mFrameCallback对象doFrame函数。

doFrame函数获取当前帧时间并传递给doAnimationFrame函数,如果执行完之后,还有动画没有执行完成则再次监听VSYNC。所以在doAnimationFrame函数中执行动画结束后肯定将AnimationFrameCallback对象从mAnimationCallbacks移除。

java 复制代码
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());
        if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);
        }
    }
};

doAnimationFrame函数遍历mAnimationCallbacks中所有AnimationFrameCallback对象,判断其是否达到开始执行动画(部分动画可能设置延时执行),到达开始时间,则调用它们的doAnimationFrame函数。

java 复制代码
private void doAnimationFrame(long frameTime) {
    long currentTime = SystemClock.uptimeMillis();
    final int size = mAnimationCallbacks.size();
    for (int i = 0; i < size; i++) {
        final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
        if (callback == null) {
            continue;
        }
        //判断动画是否达到开始执行时间
        if (isCallbackDue(callback, currentTime)) {
            callback.doAnimationFrame(frameTime);//回调AnimationFrameCallback,进行逻辑处理
            if (mCommitCallbacks.contains(callback)) {//查看在哪里注册到该map,延迟大的时候
                getProvider().postCommitCallback(new Runnable() {
                    @Override
                    public void run() {
                        //延时问题
                        commitAnimationFrame(callback, getProvider().getFrameTime());
                    }
                });
            }
        }
    }
    cleanUpList();
}

ValueAnimator对象doAnimationFrame函数。在VSYNC来临时,需要根据一些情况调整动画的开始时间mStartTime。动画未开始、恢复动画、暂停动画、动画快进、动画延迟执行都会调整动画的开始时间。如果此时动画未开始,此帧时间则是开始时间,或者开始倒计时的时间(动画设置延时执行)。如果当前是恢复动画,开始时间则更改为当前时间节点+已暂停时间(mStartTime+elapseTime),而不是停留在上次动画的帧时间。这里可能会理解为从继续上次暂停的动画效果,然后继续执行后续动画。如果此时还是第一帧时间,还需要判断是否存在动画快进的情况。

最后调用animateBasedOnTime函数计算此时此刻动画的相关属性值。参数为帧时间(开始之后)或者开始时间(刚开始)。

java 复制代码
public final boolean doAnimationFrame(long frameTime) {
  //动画刚初始化,mStartTime=-1,未开始
    if (mStartTime < 0) {
        // 第一帧动画时间,如果有延迟开始动画,此时开始倒计时
        mStartTime = mReversing
                ? frameTime
                : frameTime + (long) (mStartDelay * resolveDurationScale());
    }


    if (mPaused) {//暂停
        mPauseTime = frameTime;
        removeAnimationCallback();
        return false;
    } else if (mResumed) {//恢复
        mResumed = false;
        if (mPauseTime > 0) {
            //恢复并不是从上次动画进度节点恢复,而是以当前帧时间为开始
            mStartTime += (frameTime - mPauseTime);
        }
    }

    if (!mRunning) {
  //动画开始时间未到,并且没有快进
        if (mStartTime > frameTime && mSeekFraction == -1) {
            return false;
        } else {
            mRunning = true;
    //初始化ValueAnimator中各个属性值
            startAnimation();
        }
    }

    if (mLastFrameTime < 0) {//此次第一帧时间
        if (mSeekFraction >= 0) {
            long seekTime = (long) (getScaledDuration() * mSeekFraction);
            mStartTime = frameTime - seekTime;
            mSeekFraction = -1;
        }
        mStartTimeCommitted = false; // allow start time to be compensated for jank
    }
    mLastFrameTime = frameTime;
    //避免当前帧时间比开始时间早的问题
    final long currentTime = Math.max(frameTime, mStartTime);
//计算属性值
    boolean finished = animateBasedOnTime(currentTime);

    if (finished) {
        endAnimation();//移除FrameCallback监听,通知已注册监听
    }
    return finished;
}

我们知道动画是可以指定重复次数或无限循环的。所以animateBasedOnTime函数计算出本次帧时间来临时,本次动画进度在整体进度情况fraction。0表示刚开始执行动画;0-1表示第一次动画某个时间点;等于1表示刚好执行完第一次动画;大于1则表示开始循环动画。当大于1时,正数部分表示第几次重复动画,而小数部分表示本次动画的时间节点;正数部分如果大于上次fraction,则表示是新的循环。

这个函数有很多设计妙处。

java 复制代码
boolean animateBasedOnTime(long currentTime) {
    boolean done = false;
    if (mRunning) {
        //获取持续时间
        final long scaledDuration = getScaledDuration();
        //currentTime - mStartTime表示动画已执行完的时间长度,等于0表示刚开始,大于0,说明要跳过这部分时间。
        //(float)(currentTime - mStartTime) / scaledDuration大于1表示已经开始重复执行动画了,0-1则是第一次。1刚好第一次动画结束
        final float fraction = scaledDuration > 0 ?
                (float)(currentTime - mStartTime) / scaledDuration : 1f;
        final float lastFraction = mOverallFraction;
        //fraction在整型比上一次大,则判断为新一次循环,相等判断为同一次动画内的不同阶段
        final boolean newIteration = (int) fraction > (int) lastFraction;
        //判断是否最后一次动画循环:在有限循环次数内,本次动画等于或超出设置的总重复次数
        final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                (mRepeatCount != INFINITE);
        if (scaledDuration == 0) {
            //持续时间为0的,直接结束
            done = true;
        } else if (newIteration && !lastIterationFinished) {
            // 当前属于新的迭代次数,且还没有结束动画
            if (mListeners != null) {
                int numListeners = mListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    mListeners.get(i).onAnimationRepeat(this);
                }
            }
        } else if (lastIterationFinished) {
            done = true;//当前属于最后一次迭代
        }
        mOverallFraction = clampFraction(fraction);//缓存本次进度节点
        //先计算本次动画是第几次iteration
        //再取小数部分curFraction=fraction-iteration,根据是否反向动画,确定最终的进度curFraction or 1-curFraction
        float currentIterationFraction = getCurrentIterationFraction(
                mOverallFraction, mReversing);
        animateValue(currentIterationFraction);//通过插值器计算动画属性值
    }
    return done;
}

animateValue函数以本次动画时间进度节点,通过时间插值器变换进度节点,然后通知属性数组mValues中的对象,让它们根据变换后fraction,转化具体的属性值。然后回调UpdateListener对象的onAnimationUpdate函数。

java 复制代码
void animateValue(float fraction) {
	//插值器变换fraction
    fraction = mInterpolator.getInterpolation(fraction);
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
    	//各个属性根据fraction转化自己的值
        mValues[i].calculateValue(fraction);
    }
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
        	//回调通知
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}

这就回到了SurfaceAnimatorRunner对像startAnimationLocked函数给ValueAnimator设置监听的地方。

java 复制代码
private void startAnimationLocked(RunningAnimation a) {
    ......
    //帧动画通过valueAnimator计算产生后回调此处
    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();
    });

    ......

}

applyTransformation函数内部有调用了WindowAnimationSpec对象的apply函数。

arduino 复制代码
private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
    a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
}

apply函数获取线程本地TmpValues对象,将相关属性计算结果写到其Transformation类型的transformation成员变量。Transformation类封装Matrix、裁剪、透明度、动画类型。而Matrix可以对对象进行缩放、平移、和旋转。也就是说,Transformation类型囊括了四个动画类型缩放、平移、旋转、透明度,代表了动画某刻要应用的变换。

ini 复制代码
public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
    final TmpValues tmp = mThreadLocalTmps.get();
    //重置transformation
    tmp.transformation.clear();
    //计算某个时刻的动画属性,并将结果写会tmp.transformation
    mAnimation.getTransformation(currentPlayTime, tmp.transformation);
    //位移
    tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
    //旋转、缩放
    t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
    //透明度
    t.setAlpha(leash, tmp.transformation.getAlpha());

    boolean cropSet = false;
    if (mRootTaskClipMode == ROOT_TASK_CLIP_NONE) {
        if (tmp.transformation.hasClipRect()) {
            t.setWindowCrop(leash, tmp.transformation.getClipRect());
            cropSet = true;
        }
    } else {
        mTmpRect.set(mRootTaskBounds);
        if (tmp.transformation.hasClipRect()) {
            mTmpRect.intersect(tmp.transformation.getClipRect());
        }
        t.setWindowCrop(leash, mTmpRect);
        cropSet = true;
    }

    //设置圆角
    if (cropSet && mAnimation.hasRoundedCorners() && mWindowCornerRadius > 0) {
        t.setCornerRadius(leash, mWindowCornerRadius);
    }
}

代码中调用了AnimationgetTransformation函数,主要是计算动画的状态,是否开始、运行、结束。如果动画还是处于运行状态,则调用getTransformationAt函数。

java 复制代码
public boolean getTransformation(long currentTime, Transformation outTransformation) {
    if (mStartTime == -1) {
        mStartTime = currentTime;
    }

    final long startOffset = getStartOffset();
    final long duration = mDuration;
    float normalizedTime;
	//计算此时在本次动画的时间节点
    if (duration != 0) {
		//计算当前所处的位置0
        normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
                (float) duration;
    } else {
        //未开始或者结束了
        normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
    }
	
	//动画结束了,或者被取消。
    final boolean expired = normalizedTime >= 1.0f || isCanceled();
    mMore = !expired;

    if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

    if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
        if (!mStarted) {
            fireAnimationStart();//回调监听
            mStarted = true;
            if (NoImagePreloadHolder.USE_CLOSEGUARD) {
                guard.open("cancel or detach or getTransformation");
            }
        }
		//normalized在0f~1f之间
        if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
		//此时是否反向动画
        if (mCycleFlip) {
            normalizedTime = 1.0f - normalizedTime;
        }
		//将动画属性缓冲到outTransformation
        getTransformationAt(normalizedTime, outTransformation);
    }
	
	//结束了,但是可能循环动画
    if (expired) {
        if (mRepeatCount == mRepeated || isCanceled()) {//动画结束
            if (!mEnded) {
                mEnded = true;
                guard.close();
                fireAnimationEnd();
            }
        } else {//动画循环
            if (mRepeatCount > 0) {
                mRepeated++;
            }

            if (mRepeatMode == REVERSE) {
                mCycleFlip = !mCycleFlip;
            }

            mStartTime = -1;
            mMore = true;

            fireAnimationRepeat();
        }
    }

    if (!mMore && mOneMoreTime) {
        mOneMoreTime = false;
        return true;
    }

    return mMore;
}

getTransformationAt函数内部又调用AnimationapplyTransformation函数。这里的Animation子类会重写该函数,实现自己的特定属性计算。Animation子类可以是在主题或者指定的Animation为准,如前面由系统默认指定的动画。

java 复制代码
public void getTransformationAt(float normalizedTime, Transformation outTransformation) {
    final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
    applyTransformation(interpolatedTime, outTransformation);
}

动画的展示

回到了SurfaceAnimatorRunner对像startAnimationLocked函数给ValueAnimator设置监听的地方。在上面执行了applyTransformation函数之后,调用了applyTransformation。

java 复制代码
private final Runnable mApplyTransactionRunnable = this::applyTransaction;

private void scheduleApplyTransaction() {
    if (!mApplyScheduled) {
        mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
                null /* token */);
        mApplyScheduled = true;
    }
}

private void applyTransaction() {
    mFrameTransaction.setAnimationTransaction();
    mFrameTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
    mFrameTransaction.apply();
    mApplyScheduled = false;
}

根据前面mSurfaceAnimationRunner对象来自WMS的成员变量,其在WMS创建的时候被创建,Transaction类型的mFrameTransaction对象以同步方式设置SurfaceController。applyTransaction函数调用mFrameTransaction对象的几个方法,内部都调用底层代码去设置Surface。这里不再进一步跟进。

总结

通过本文对动画源码的学习,了解在动画上面对Float浮点数巧妙运用,用来合理控制动画的重复行为和单次进度。

同时也了解到动画在底层也是通过ValueAnimator来计算动画的各个属性 ,因此进一步确定原理,动画就是在指定的时间内播放一帧帧图像,根据人眼对图像的感知特色,从而形成动效。

另外,动画会以垂直信号来初始化动画和触发动画的开始。

相关推荐
guoruijun_2012_42 小时前
fastadmin多个表crud连表操作步骤
android·java·开发语言
Winston Wood2 小时前
一文了解Android中的AudioFlinger
android·音频
B.-3 小时前
Flutter 应用在真机上调试的流程
android·flutter·ios·xcode·android-studio
有趣的杰克3 小时前
Flutter【04】高性能表单架构设计
android·flutter·dart
大耳猫9 小时前
主动测量View的宽高
android·ui
帅次11 小时前
Android CoordinatorLayout:打造高效交互界面的利器
android·gradle·android studio·rxjava·android jetpack·androidx·appcompat
枯骨成佛12 小时前
Android中Crash Debug技巧
android
kim565917 小时前
android studio 更改gradle版本方法(备忘)
android·ide·gradle·android studio
咸芝麻鱼17 小时前
Android Studio | 最新版本配置要求高,JDK运行环境不适配,导致无法启动App
android·ide·android studio
无所谓จุ๊บ17 小时前
Android Studio使用c++编写
android·c++