属性动画原理-动画的启动
2022-06-09 本文已影响0人
毛先森
前言
Android 的动画分为帧动画,补间动画,属性动画,这些概念都是老生常谈了,那它们底层的逻辑到底是怎样的呢,我们这次透过表面看本质,到底内部是怎么处理动画的
简单的动画大概有移动/缩放/旋转等几种类型,属性动画中是怎样处理这些的呢?
我们化繁为简,从动画的启动开始入手,了解 Choreographer 是怎样转换 VSYNC 信号的
提出问题
- VSYNC 是什么 ?
- Choreographer 在动画 start() 中,实现了什么功能
源码分析
从 ObjectAnimator.ofInt(mButton, "width", 500).setDuration (5000).start() 入手
- ofInt() 做了什么
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
// 这里只是创建 ObjectAnimator 对象
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setIntValues(values);
return anim;
}
private ObjectAnimator(Object target, String propertyName) {
//设置动画的目前 View
setTarget(target);
setPropertyName(propertyName);
}
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
// 取消之前的
if (isStarted()) {
cancel();
}
// 重新设置 targetView ,这里使用了弱引用
mTarget = target == null ? null : new WeakReference<Object>(target);
mInitialized = false;
}
}
public void setPropertyName(@NonNull String propertyName) {
if (mValues != null) {
// 移除掉HashMap的旧值,至于为什么要这样,等会再看看
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
//当前的属性 put 到表中 mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
mInitialized = false;
}
- start() 做了什么
public void start() {
// AnimationHandler 需要了解下功能
// 这里将之前取消掉,然后调用父类的 start()
AnimationHandler.getInstance().autoCancelBasedOn(this);
super.start();
}
/**
ObjectAnimator 的父类 VauleAnimator
**/
private void start(boolean playBackwards) {
// 一般不会,因为在主线程执行
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
// 是否重复执行
mReversing = playBackwards;
mSelfPulse = !mSuppressSelfPulseRequested;
// AnimationHandler.addAnimationFrameCallback()
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
// 触发 onAnimationStart()
startAnimation();
if (mSeekFraction == -1) {
// No seek, start at play time 0. Note that the reason we are not using fraction 0
// is because for animations with 0 duration, we want to be consistent with pre-N
// behavior: skip to the final value immediately.
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
/**
AnimationHandler.class
**/
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
// 根据是否有延时执行,来放到不同的动画任务队列中
// 若队列为空,则直接 post
if (mAnimationCallbacks.size() == 0) {
// 这里实际是调用 Choreographer.scheduleFrameLocked(),发送一条异步消息
getProvider().postFrameCallback(mFrameCallback);
}
// 队列不为空,就往队尾插入
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
// 延时执行,则放到另外的队列中
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
有点绕了,先整理下思路,在调用开始后,AnimationHandler 会创建一个 Callback,放到队列中,或者直接发送 Msg,这里的作用是什么呢?
实际上最终是向 native 注册 VSYNC 信号的监听,通过 DisplayEventReceiver.scheduleVsync() 注册的,当 VSYNC 信号到来时,会触发 DisplayEventReceiver.dispatchVsync,进而触发 FrameDisplayEventReceiver.onVsync()
// Called from native code.
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
onVsync(timestampNanos, physicalDisplayId, frame);
}
// 这里将 native 层的 VSYNC ,分发到 Java 层中
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
if (timestampNanos > now) {
timestampNanos = now;
}
if (mHavePendingVsync) {
Log.w(TAG, "Already have a pending vsync event. There should only be "
+ "one at a time.");
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
// 发送异步消息
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
VSYNC 是什么 ?
VSYNC 是垂直同步的意思,在 Android 应用层意味着屏幕刷新,具体的 VSYNC 机制参考 Vsync 信号机制和 UI 刷新流程
Choreographer 在动画 start() 中,实现了什么功能
注册监听 VSYNC 信号,并且在信号到来后,在 Java 层发送通知
Choreographer 原理
這個单独开一篇文章来讲