分析下面一段代码的逻辑

objectAnimator.start();

他会调用父类的start(),即ValueAnimator,我们分析valueAnimator.start()即可

ValueAnimator:

public void start() {
start(false);
}
private void start(boolean playBackwards) {

       ...

        AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this); ... animationHandler.start();
}

ValueAnimator把动画逻辑交给了AnimationHandler。接着看animationHandler.start()

ValueAnimator.AnimationHandler:

public void start() {
scheduleAnimation();
}
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
mAnimationScheduled = true;
}
}
mChoreographer是什么?这追溯到AnimationHandler的创建地方。
首先看AnimationHandler创建的机制
ValueAnimator:
private static AnimationHandler getOrCreateAnimationHandler() {
AnimationHandler handler = sAnimationHandler.get();
if (handler == null) {
handler = new AnimationHandler();
sAnimationHandler.set(handler);
}
return handler;
}
protected static ThreadLocal<AnimationHandler> sAnimationHandler =
new ThreadLocal<AnimationHandler>();

所以一个线程中只有一个AnimationHandler。

接着看AnimationHandler创建细节

ValueAnimator.AnimationHandler:

private AnimationHandler() {
mChoreographer = Choreographer.getInstance();
}

接着看Choreographer:

Choreographer:

public static Choreographer getInstance() {
return sThreadInstance.get();
}
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
return new Choreographer(looper);
}
};

可见Choreographer也是每个线程只有一个,而且他指明了只有在具有looper的线程下才能创建成功,这是因为他会创建一个handler

Choreographer:

private Choreographer(Looper looper) {
mLooper = looper;
mHandler = new FrameHandler(looper); ... }

Choreographer.FrameHandler:

private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
} @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}

我们再回头看看Choreographer是如何使动画跑起来的:

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[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);
}
}
}

可以知道他把action存储到了mCallbackQueues数组中的下标为CALLBACK_ANIMATION(1)的CallbackQueue中。

因为delayMillis为0,所以他会发送一个what为MSG_DO_SCHEDULE_CALLBACK的Message到mHandler去处理。

根据上面的what分支看下去

Choreographer:

void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
} // If running on the Looper thread, then schedule the vsync immediately,
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
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);
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}

USE_VSYNC是垂直同步的意思,使得显卡生成帧的速度和屏幕刷新的速度的保持一致。在此不讨论其细节,假设不开启,因为,如果开启的话,后面的流程最终还是和不开启的流程一样的。

接着看MSG_DO_FRAME分支

Choreographer:

void doFrame(long frameTimeNanos, int frame) {
... try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame"); 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 {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
} ...
}
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
// We use "now" to determine when callbacks become due because it's possible
// for earlier processing phases in a frame to post callbacks that should run
// in a following phase, such as an input event that causes an animation to start.
final long now = System.nanoTime();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
...
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
if (DEBUG_FRAMES) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}

首先取出CALLBACK_ANIMATION对应的CallbackQueue中的actions列表,然后运行。这样,我们就回到了刚开始使用Choreographer时传入的action。

ValueAnimator.AnimationHandler:

final Runnable mAnimate = new Runnable() {
@Override
public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
};
void doAnimationFrame(long frameTime) {
mLastFrameTime = frameTime; // mPendingAnimations holds any animations that have requested to be started
// We're going to clear mPendingAnimations, but starting animation may
// cause more to be added to the pending list (for example, if one animation
// starting triggers another starting). So we loop until mPendingAnimations
// is empty.
while (mPendingAnimations.size() > 0) {
ArrayList<ValueAnimator> pendingCopy =
(ArrayList<ValueAnimator>) mPendingAnimations.clone();
mPendingAnimations.clear();
int count = pendingCopy.size();
for (int i = 0; i < count; ++i) {
ValueAnimator anim = pendingCopy.get(i);
// If the animation has a startDelay, place it on the delayed list
if (anim.mStartDelay == 0) {
anim.startAnimation(this);
} else {
mDelayedAnims.add(anim);
}
}
} // Next, process animations currently sitting on the delayed queue, adding
// them to the active animations if they are ready
int numDelayedAnims = mDelayedAnims.size();
for (int i = 0; i < numDelayedAnims; ++i) {
ValueAnimator anim = mDelayedAnims.get(i);
if (anim.delayedAnimationFrame(frameTime)) {
mReadyAnims.add(anim);
}
}
int numReadyAnims = mReadyAnims.size();
if (numReadyAnims > 0) {
for (int i = 0; i < numReadyAnims; ++i) {
ValueAnimator anim = mReadyAnims.get(i);
anim.startAnimation(this);
anim.mRunning = true;
mDelayedAnims.remove(anim);
}
mReadyAnims.clear();
} // Now process all active animations. The return value from animationFrame()
// tells the handler whether it should now be ended
int numAnims = mAnimations.size();
for (int i = 0; i < numAnims; ++i) {
mTmpAnimations.add(mAnimations.get(i));
}
for (int i = 0; i < numAnims; ++i) {
ValueAnimator anim = mTmpAnimations.get(i);
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
mTmpAnimations.clear();
if (mEndingAnims.size() > 0) {
for (int i = 0; i < mEndingAnims.size(); ++i) {
mEndingAnims.get(i).endAnimation(this);
}
mEndingAnims.clear();
} ...
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
}

ValueAnimator:

private void startAnimation(AnimationHandler handler) {
...
initAnimation();
handler.mAnimations.add(this);
...
}

doAnimationFrame遍历mPendingAnimations,需要执行的ValueAnimator会添加到mAnimations中,遍历mAnimations,然后开始执行真正的动画操作

ValueAnimator:

final boolean doAnimationFrame(long frameTime) {
...
return animationFrame(currentTime);
}
boolean animationFrame(long currentTime) {
boolean done = false;
switch (mPlayingState) {
case RUNNING:
case SEEKED:
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
...
animateValue(fraction);
break;
} 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);
}
}
}

animateValue方法就是ValueAnimator的核心

  1. 首先根据当前的Interpolator得到对应的fraction。
  2. 然后遍历mValues,执行calculateValue。mValues是一个PropertyValuesHolder集合,PropertyValuesHolder存储着需要动态变化的信息,比如方法名的字符串对象"translationX",这就是我们平时ObjectAnimator.ofFloat提供的方法名啊。执行calculateValue就是利用反射来执行对应的方法(这个细节我还没有仔细看源码),实现真正的动画。
  3. 最后遍历监听器并回调。

动画如何结束呢?我们看回去AnimationHandler的doAnimationFrame方法,里面有这么一段代码:

ValueAnimator.AnimationHandler.doAnimationFrame:

for (int i = 0; i < numAnims; ++i) {
ValueAnimator anim = mTmpAnimations.get(i);
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
mTmpAnimations.clear();
if (mEndingAnims.size() > 0) {
for (int i = 0; i < mEndingAnims.size(); ++i) {
mEndingAnims.get(i).endAnimation(this);
}
mEndingAnims.clear();
}

ValueAnimator:

protected void endAnimation(AnimationHandler handler) {
handler.mAnimations.remove(this);
handler.mPendingAnimations.remove(this);
handler.mDelayedAnims.remove(this);
mPlayingState = STOPPED;
mPaused = false;
...
}

可见,一个动画如果没有完成就不会添加到mEndingAnims列表,一旦完成了就会加入,并且会被删除掉。自然这个动画就算结束了。

mAnimations只要不为空,那么就会再次调用scheduleAnimation(),如下

ValueAnimator.AnimationHandler.doAnimationFrame:

if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}

总结一下:

  1. ValueAnimator创建时,会获取到本线程的一个AnimationHandler,里面包含一个本线程的Choreographer,Choreographer又包含一个handler(所以要求ValueAnimator创建所在的线程必须是具有looper的)。
  2. ValueAnimator通过AnimationHandler执行动画,AnimationHandler又通过Choreographer中的handler进行不断的回调,ValueAnimator收到回调后利用反射机制执行动画操作。
  3. ObjectAnimator作用的对象如果只能在特定的线程里面操作,ObjectAnimator必须在特定的线程创建,这样才能在特定的线程得到Choreographer中的handler的回调。(比如View只能在主线程操作UI更新)

ObjectAnimator.start()工作原理的更多相关文章

  1. 菜鸟学Struts2——Struts工作原理

    在完成Struts2的HelloWorld后,对Struts2的工作原理进行学习.Struts2框架可以按照模块来划分为Servlet Filters,Struts核心模块,拦截器和用户实现部分,其中 ...

  2. 【夯实Nginx基础】Nginx工作原理和优化、漏洞

    本文地址 原文地址 本文提纲: 1.  Nginx的模块与工作原理    2.  Nginx的进程模型    3 . NginxFastCGI运行原理        3.1 什么是 FastCGI   ...

  3. HashMap的工作原理

    HashMap的工作原理   HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和HashMap之间 ...

  4. 【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之RAC 工作原理和相关组件(三)

    RAC 工作原理和相关组件(三) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总.然后形成体 ...

  5. ThreadLocal 工作原理、部分源码分析

    1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...

  6. Servlet的生命周期及工作原理

    Servlet生命周期分为三个阶段: 1,初始化阶段  调用init()方法 2,响应客户请求阶段 调用service()方法 3,终止阶段 调用destroy()方法 Servlet初始化阶段: 在 ...

  7. 代码管理工具 --- git的学习笔记二《git的工作原理》

    通过几个问题来学习代码管理工具之git 一.git是什么?为什么要用它?使用它的好处?它与svn的区别,在Mac上,比较好用的git图形界面客户端有 git 是分布式的代码管理工具,使用它是因为,它便 ...

  8. 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL

    周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...

  9. 浏览器内部工作原理--作者:Tali Garsiel

    本篇内容为转载,主要用于个人学习使用,作者:Tali Garsiel 一.介绍 浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工作原理,我们将看到,从你在地址栏输入google.com到你看到 ...

随机推荐

  1. 【记录】ASP.NET XSS 脚本注入攻击

    输入进行 Html 转码: HttpUtility.HtmlEncode(content); 输入保留 Html 标记,使用 AntiXSS 过滤: Install-Package AntiXSS M ...

  2. VMware网络设置详解--不错

    我们知道,VMware Workstation提供了很多虚拟设备,利用这些设备,我们除了可以组建典型的桥接网络.仅主机网络.NAT网络外,还能组建复杂的自定义网络.本篇 目的就是让大家认识和掌握VMw ...

  3. JavaMail发送邮件的笔记及Demo

    最近碰到一个需求,就是注册用户时候需要向用户发送激活邮箱,于是照着网上搜来的demo自己试着运行了一下,发件时我用的是网易163邮箱,收件时用QQ邮箱,运行后报了一个错误: 网络上搜索解决方式,多次尝 ...

  4. C#解决Linq OrderBy() 失效的小技巧

    前言 前几天的一个数据列表中我用了Linq GroupBy 和OrderBy. 排序在本机正常使用,发到测试后排序死活不对,很是郁闷,总以为是程序问题.于是请教了另外一个同事.有了以下的答案. 问题原 ...

  5. 4.Struts2转向类型详解

    struts2中提供了多种视图转向类型,类型由type属性指定,如: dispatcher:请求转发(默认值) redirect:重定向到页面 redirectAction:重定向到Action pl ...

  6. 基于Metronic的Bootstrap开发框架经验总结(10)--优化Bootstrap图标管理

    在基于Bootstrap开发的项目中,鲜艳颜色的按钮,以及丰富的图表是很吸引人的特点,为了将这个特点发挥到极致,可以利用Bootstrap图标抽取到数据库里面,并在界面中进行管理和使用,这样我们可以把 ...

  7. 【C#进阶系列】30 学习总结

    前面学起来还是很顺的,毕竟很多都接触过. 后面学起来只能用“磨”来形容,以至于八章用了2个月.(当然也有相当一些原因是这两个月中发生了一些个人生活上的问题) 总的来说收获超大,这种感觉就像大一的时候学 ...

  8. qam 64的设计

    module qam64(x,out,clk,clk1,rst);input x,clk,clk1,rst;output [18:0] out;reg [2:0] count;reg [5:0] re ...

  9. Linux安装JDK1.7

    发表此篇文章纯属本人愚钝,希望以后再安装JDK不要走那么多曲折的路,也希望可以给后人借鉴. 1.以下以JDK1.7为例 具体官网地址:http://www.oracle.com/technetwork ...

  10. ABP中使用OAuth2(Resource Owner Password Credentials Grant模式)

    ABP目前的认证方式有两种,一种是基于Cookie的登录认证,一种是基于token的登录认证.使用Cookie的认证方式一般在PC端用得比较多,使用token的认证方式一般在移动端用得比较多.ABP自 ...