ValueAnimation 是刷新的
ValueAnimation 是如何刷新的
背景
之前,技术分享讲过ValueAnimation底层源码。但是,没有提到,Animation的更新。此篇文章,聚焦于ValueAnimation start方法开始,到Choreographer的响应回调。
ValueAnimator部分
ValueAnimator.start()
从思维逻辑上,start方法是动画启动的入口,那绘制应该也从这里开始寻找
//ValueAnimator
@Override
public void start() {
start(false);
}
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mReversing = playBackwards;
mSelfPulse = !mSuppressSelfPulseRequested;
// Special case: reversing from seek-to-0 should act as if not seeked at all.
if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
if (mRepeatCount == INFINITE) {
// 计算当前迭代的分数。
float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
mSeekFraction = 1 - fraction;
} else {
mSeekFraction = 1 mRepeatCount - mSeekFraction;
}
}
mStarted = true;
mPaused = false;
mRunning = false;
mAnimationEndRequested = false;
// 当start()被调用时,重置mLastFrameTime,这样如果动画正在运行,
// 调用start()将把动画放到已经开始但尚未到达的第一帧阶段。
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
// 如果没有启动延迟,初始化动画并立即通知启动监听器,
// 以与前面的行为保持一致。否则,将此延迟到开始延迟后的第一帧。
startAnimation();
if (mSeekFraction == -1) {
// 无seek, 0时开始。注意,我们没有使用分数0的原因是,对于持续时间为0的动画,
// 我们希望与前n的行为保持一致:立即跳到最终值。
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
需求要注意的是,在startAnimation
方法中进行数值的初始化,和Linstener
的调用。但是不在本篇的范围内,不做分析。其中重点是addAnimationCallback
方法。
//ValueAnimator
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
public AnimationHandler getAnimationHandler() {
return mAnimationHandler != null ? mAnimationHandler : AnimationHandler.getInstance();
}
AnimationHandler
可以看到,addAnimationCallback
方法,最终调用到的是AnimationHandler
的addAnimationFrameCallback
方法。
//AnimationHandler
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
//如果之前没有动画要展示,就找provider启动动画循环(假循环)
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
//之前不存在,就往进加
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() delay));
}
}
注释很清楚,我们应该往getProvider().postFrameCallback
里看。
//AnimationHandler
private AnimationFrameCallbackProvider getProvider() {
if (mProvider == null) {
mProvider = new MyFrameCallbackProvider();
}
return mProvider;
}
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
final Choreographer mChoreographer = Choreographer.getInstance();
//就是这里
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
@Override
public void postCommitCallback(Runnable runnable) {
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
}
@Override
public long getFrameTime() {
return mChoreographer.getFrameTime();
}
@Override
public long getFrameDelay() {
return Choreographer.getFrameDelay();
}
@Override
public void setFrameDelay(long delay) {
Choreographer.setFrameDelay(delay);
}
}
我们可以看到,就是往Choreographer
里面塞回调。这个Choreographer
我们后期会讲到,我们目前只需要知道,当Choreographer
收到Vsync
后,会依次回调Callback就好了,其中调用onFrame
方法。
这里塞的是什么呢?mFrameCallback
//AnimationHandler
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
我们看到,会调用,doAnimationFrame
//AnimationHandler
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。
callback.doAnimationFrame(frameTime);
if (mCommitCallbacks.contains(callback)) {
getProvider().postCommitCallback(new Runnable() {
@Override
public void run() {
commitAnimationFrame(callback, getProvider().getFrameTime());
}
});
}
}
}
cleanUpList();
}
我们先关注callback.doAnimationFrame
方法。这个callback
是什么呢?getAnimationHandler().addAnimationFrameCallback(this, delay);
。其实就是这个this嘛。我们应该往ValueAnimation
里面找。
//ValueAnimator
public final boolean doAnimationFrame(long frameTime) {
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 {
// If mRunning is not set by now, that means non-zero start delay,
// no seeking, not reversing. At this point, start delay has passed.
mRunning = true;
startAnimation();
}
}
if (mLastFrameTime < 0) {
if (mSeekFraction >= 0) {
long seekTime = (long) (getScaledDuration() * mSeekFraction);
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
mStartTimeCommitted = false;
}
mLastFrameTime = frameTime;
//就是在这里形成了数据更新
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
//关注这里,当动画结束,怎么操作的。
endAnimation();
}
return finished;
}
在这里,我们关注的应该只有两个点,一个就是动画的更新,一个就是动画结束的处理。
动画更新
//ValueAnimator
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
final long scaledDuration = getScaledDuration();
final float fraction = scaledDuration > 0 ?
(float)(currentTime - mStartTime) / scaledDuration : 1f;
final float lastFraction = mOverallFraction;
final boolean newIteration = (int) fraction > (int) lastFraction;
final boolean lastIterationFinished = (fraction >= mRepeatCount 1) &&
(mRepeatCount != INFINITE);
//判断是否结束啥的
if (scaledDuration == 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);
//计算当前迭代的部分,并考虑动画是否应该向后播放。当动画在迭代中向后播放时,迭代的分数将从1f到0f。
float currentIterationFraction = getCurrentIterationFraction(
mOverallFraction, mReversing);
//刷新
animateValue(currentIterationFraction);
}
return done;
}
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; i) {
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; i) {
//回调 - 我们在里面做我们设置的骚操作
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
注释写的很清楚,在这里,ValueAnimation就得到了更新
动画结束
animateBasedOnTime
的返回值,决定了,这个动画是否结束。如果结束了就执行,endAnimation()
。
//ValueAnimator
private void endAnimation() {
if (mAnimationEndRequested) {
return;
}
//从AnimationHandler移除Callback
removeAnimationCallback();
mAnimationEndRequested = true;
mPaused = false;
boolean notify = (mStarted || mRunning) && mListeners != null;
if (notify && !mRunning) {
// If it's not yet running, then start listeners weren't called. Call them now.
notifyStartListeners();
}
mRunning = false;
mStarted = false;
mStartListenersCalled = false;
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
if (notify && mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; i) {
//通知回调
tmpListeners.get(i).onAnimationEnd(this, mReversing);
}
}
// mReversing needs to be reset *after* notifying the listeners for the end callbacks.
mReversing = false;
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
}
private void removeAnimationCallback() {
if (!mSelfPulse) {
return;
}
getAnimationHandler().removeCallback(this);
}
我们看到,在endAnimation
中,如果动画结束,就通知删除回调。
doFrame
到这里,AnimationHandler
的doAnimationFrame
就分析完了。我们回到mFrameCallback
。
//AnimationHandler
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
里,如果动画已经结束了,就会从AnimationHandler
中删除Callback
, 这个Callback
就放在mAnimationCallbacks
中,这个mAnimationCallbacks.size() > 0
的判断就很合理了。如果还有动画没有执行完成,就注册下一次Choreographer
收到Vsync
的刷新。
总结
ValueAnimator
驱动Choreographer
监听Vsync
,把回调交给Chreographer
,等待下次刷新的时候,刷新自己。
AnimationHanlder
相当于是一个分发中心。当有动画要执行,就获取一个全局的变量,从AnimationHanlder
获取的位置,即AnimationHandler.getInstance()
,可以看出。各个动画,也不用直接操作Chreographer
,交给AnimationHanlder
就可以了。AnimationHanlder
会在Vsync
到的时候,被Chreographer
通知,来调用注册的各个动画更新。
既然,ValueAnimator
和AnimationHanlder
,推动Chreographer
注册下一次Vsync
,引起进度更新,那怎么引起页面的刷新呢?
ViewRootImpl 部分
ValueAnimator使用时
private final ValueAnimator.AnimatorUpdateListener listener = animation -> {
progress = (Float) animation.getAnimatedValue();
postInvalidate();
};
animator = ValueAnimator.ofFloat(ShrinkModel.PROGRESS_START, ShrinkModel.PROGRESS_END);
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(duration);
animator.addUpdateListener(listener);
animator.start();
我们可以看到,当onAnimationUpdate
时,我们手动调用了postInvalidate()
,postInvalidate()
相当于在UI线程调用invalidate()
。这个是同事写的代码。当然我们可以直接写invalidate()
。
//View
public void postInvalidate() {
postInvalidateDelayed(0);
}
//View
public void postInvalidateDelayed(long delayMilliseconds) {
//调到ViewRootImpl
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
}
}
//ViewRootImpl
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
//往Handler里放,这个Handler绑定的UI线程
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
//ViewRootImpl
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
//剩下的忽略
}
}
view.invalidata时
当View
调用invalidata
时,会调用到ViewRootImpl
的invalidateChildInParent
,为什么会这样?
不熟悉绘制流程的,应该去补习了。另外检验自己懂不懂的标准就是,能不能回答出postInvalidata
、invalidata
、requestLayout
的区别。
但是提两个点,在Activity
的resume
后,会讲DecorView
放进PhoneWindow
,通过WindowManager.addView
方法。在这里,最终会调用到ViewRootImpl.setView
。在里面,会把自己赋值为DecorView
的Parent
。所以,不管是invalidata
还是requestLayout
,都会调用到ViewRootImpl
。
//ViewRootImpl
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " dirty);
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
invalidateRectOnScreen(dirty);
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
// Add the new dirty rect to the current one
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
// Intersect with the bounds of the window to skip
// updates that lie outside of the visible region
final float appScale = mAttachInfo.mApplicationScale;
final boolean intersected = localDirty.intersect(0, 0,
(int) (mWidth * appScale 0.5f), (int) (mHeight * appScale 0.5f));
if (!intersected) {
localDirty.setEmpty();
}
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();
}
}
我们看到,如果没有特殊情况,会走scheduleTraversals
。这个方法,大家应该很熟了吧。引发绘制嘛。
ViewRootImpl.scheduleTraversals
//ViewRootImpl
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//防止同步消息屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//向Choreographer注册Vsync回调
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
这里有两个比较重要的内容:
- 同步消息屏障
- Choreographer注册Vsync回调
我们先看回调吧,当Vsync
来的时候,ViewRootImpl
会怎么走
//ViewRootImpl
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//解除同步消息屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//里面进行 m - l - d,页面就重绘了。
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
我们可以看到,当Vsync
到的时候,会解除同步消息屏障。再触发重绘。
下面我们来看看Choreographer
Choreographer部分
postCallback
//Choreographer
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
//去掉了错误检查
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now delayMillis;
//mCallbackQueues是一个回调队列。数组加链表的结构。
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
//如果理解执行,就跑这个
scheduleFrameLocked(now);
} else {
//不立即执行,就放一个异步消息,保证优先执行
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (isRunningOnLooperThreadLocked()) {
//使用了垂直消息同步,还通过了检查,就这么跑
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS sFrameDelay, now);
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
//
public void scheduleVsync() {
if (mReceiverPtr == 0) {
} else {
//向Native请求要来的Vsync。并且,只会回调一次
nativeScheduleVsync(mReceiverPtr);
}
}
当Vsyn
到来的时候,会调用FrameDisplayEventReceiver
的onVsync
方法
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
//去掉构造和本地变量
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
//这些都是特殊情况
if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
scheduleVsync();
return;
}
long now = System.nanoTime();
if (timestampNanos > now) {
timestampNanos = now;
}
if (mHavePendingVsync) {
} else {
mHavePendingVsync = true;
}
//这里才是正常流程
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
我们可以看到,最总会走到doFrame
方法
doFrame
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
mDebugPrintNextFrameTimeDelta = false;
}
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
}
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
scheduleVsyncLocked();
return;
}
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
scheduleVsyncLocked();
return;
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
try {
//在这里,处理的消息回调 - 包括动画和绘制
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhibceje
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22 -
excel打印预览压线压字怎么办
PHP中文网 06-22