属性动画的启动我们是通过如下来完成的:
objectAnimator.start();//动画开始时从start方法开始的
start()
核心代码如下:
AnimationHandler.getInstance().autoCancelBasedOn(this);
super.start();
start()
方法主要做了两件事,一个是检测如果动画已经执行,则停止动画;另一方面调用了父类的start()方法。
ObjectAnimator
的父类是ValueAnimator
,它的start()
方法如下:
start(false);
我们接着看下ValueAnimator
的这个方法,核心代码如下:
//开启动画的线程要有Looper对象=》动画只能在主线程中运行
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
startAnimation(); //如果没有启动延迟,就立即启动动画
if (mSeekFraction == -1) {
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
这里主要看addAnimationCallback(0);
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
//添加一个AnimationFrameCallback,ValueAnimator实现了AnimationFrameCallback接口
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
流程走到这里,需要看两个方法:
- getAnimationHandler()
看下getAnimationHandler()
这个方法:
public AnimationHandler getAnimationHandler() {
return AnimationHandler.getInstance();//getInstance()这个方法
}
AnimationHandler
不是Handler,而是AnimationHandler用ThreadLocal保证每个线程只有一个实例。做到线程中单例,“getInstance()”方法以及实现如下:
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
private boolean mListDirty = false;
public static AnimationHandler getInstance() {
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
return sAnimatorHandler.get();
}
- addAnimationFrameCallback
addAnimationFrameCallback
的核心代码:
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
}
AnimationFrameCallback
是ValueAnimator
的父类,getProvider()
返回的是MyFrameCallbackProvider,我们来看下postFrameCallback
这个方法,通过不断的调用跳转,我们来到了Choreographer
的postCallbackDelayedInternal
这个方法:
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {//这个action就是callback
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
//没有延迟,直接开始调度
scheduleFrameLocked(now);
} else {
//有延迟,就通过handler延时发送一个message
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
看下scheduleFrameLocked(now);
这个方法:,核心代码如下:
if (USE_VSYNC) {//为动画和绘图启动/禁用垂直同步信号
//如果是在主线程就直接调度,否则就发送一个消息到主线程
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
}
然后看下这个方法scheduleVsyncLocked
,通过不断的跳转,我们可以看到在DisplayEventReceiver
这个类中的scheduleVsync
方法如下:
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
nativeScheduleVsync
方法其实是一个native方法,这个方法其实是向native层注册成为观察者,用来接收屏幕刷新信号。
private static native void nativeScheduleVsync(long receiverPtr);
总结:start
方法中检测如果动画已经执行,则停止动画,并且限定了动画只能在主线程中运行,动画如果在主线程中运行,那么将会判断有没有启动延迟,如果没有就立即执行动画,通过AnimationHandler
用ThreadLocal
保证每个线程只有一个实例。做到线程中单例。执行动画后,会为动画和绘图启动垂直同步信号,通过向native
曾注册成为观察者,用来接收屏幕刷新信号。
网友评论