ObjectAnimator.start()工作原理
分析下面一段代码的逻辑
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的核心
- 首先根据当前的Interpolator得到对应的fraction。
- 然后遍历mValues,执行calculateValue。mValues是一个PropertyValuesHolder集合,PropertyValuesHolder存储着需要动态变化的信息,比如方法名的字符串对象"translationX",这就是我们平时ObjectAnimator.ofFloat提供的方法名啊。执行calculateValue就是利用反射来执行对应的方法(这个细节我还没有仔细看源码),实现真正的动画。
- 最后遍历监听器并回调。
动画如何结束呢?我们看回去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();
}
总结一下:
- ValueAnimator创建时,会获取到本线程的一个AnimationHandler,里面包含一个本线程的Choreographer,Choreographer又包含一个handler(所以要求ValueAnimator创建所在的线程必须是具有looper的)。
- ValueAnimator通过AnimationHandler执行动画,AnimationHandler又通过Choreographer中的handler进行不断的回调,ValueAnimator收到回调后利用反射机制执行动画操作。
- ObjectAnimator作用的对象如果只能在特定的线程里面操作,ObjectAnimator必须在特定的线程创建,这样才能在特定的线程得到Choreographer中的handler的回调。(比如View只能在主线程操作UI更新)
ObjectAnimator.start()工作原理的更多相关文章
- 菜鸟学Struts2——Struts工作原理
在完成Struts2的HelloWorld后,对Struts2的工作原理进行学习.Struts2框架可以按照模块来划分为Servlet Filters,Struts核心模块,拦截器和用户实现部分,其中 ...
- 【夯实Nginx基础】Nginx工作原理和优化、漏洞
本文地址 原文地址 本文提纲: 1. Nginx的模块与工作原理 2. Nginx的进程模型 3 . NginxFastCGI运行原理 3.1 什么是 FastCGI ...
- HashMap的工作原理
HashMap的工作原理 HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和HashMap之间 ...
- 【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之RAC 工作原理和相关组件(三)
RAC 工作原理和相关组件(三) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总.然后形成体 ...
- ThreadLocal 工作原理、部分源码分析
1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...
- Servlet的生命周期及工作原理
Servlet生命周期分为三个阶段: 1,初始化阶段 调用init()方法 2,响应客户请求阶段 调用service()方法 3,终止阶段 调用destroy()方法 Servlet初始化阶段: 在 ...
- 代码管理工具 --- git的学习笔记二《git的工作原理》
通过几个问题来学习代码管理工具之git 一.git是什么?为什么要用它?使用它的好处?它与svn的区别,在Mac上,比较好用的git图形界面客户端有 git 是分布式的代码管理工具,使用它是因为,它便 ...
- 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL
周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...
- 浏览器内部工作原理--作者:Tali Garsiel
本篇内容为转载,主要用于个人学习使用,作者:Tali Garsiel 一.介绍 浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工作原理,我们将看到,从你在地址栏输入google.com到你看到 ...
随机推荐
- 【记录】ASP.NET XSS 脚本注入攻击
输入进行 Html 转码: HttpUtility.HtmlEncode(content); 输入保留 Html 标记,使用 AntiXSS 过滤: Install-Package AntiXSS M ...
- VMware网络设置详解--不错
我们知道,VMware Workstation提供了很多虚拟设备,利用这些设备,我们除了可以组建典型的桥接网络.仅主机网络.NAT网络外,还能组建复杂的自定义网络.本篇 目的就是让大家认识和掌握VMw ...
- JavaMail发送邮件的笔记及Demo
最近碰到一个需求,就是注册用户时候需要向用户发送激活邮箱,于是照着网上搜来的demo自己试着运行了一下,发件时我用的是网易163邮箱,收件时用QQ邮箱,运行后报了一个错误: 网络上搜索解决方式,多次尝 ...
- C#解决Linq OrderBy() 失效的小技巧
前言 前几天的一个数据列表中我用了Linq GroupBy 和OrderBy. 排序在本机正常使用,发到测试后排序死活不对,很是郁闷,总以为是程序问题.于是请教了另外一个同事.有了以下的答案. 问题原 ...
- 4.Struts2转向类型详解
struts2中提供了多种视图转向类型,类型由type属性指定,如: dispatcher:请求转发(默认值) redirect:重定向到页面 redirectAction:重定向到Action pl ...
- 基于Metronic的Bootstrap开发框架经验总结(10)--优化Bootstrap图标管理
在基于Bootstrap开发的项目中,鲜艳颜色的按钮,以及丰富的图表是很吸引人的特点,为了将这个特点发挥到极致,可以利用Bootstrap图标抽取到数据库里面,并在界面中进行管理和使用,这样我们可以把 ...
- 【C#进阶系列】30 学习总结
前面学起来还是很顺的,毕竟很多都接触过. 后面学起来只能用“磨”来形容,以至于八章用了2个月.(当然也有相当一些原因是这两个月中发生了一些个人生活上的问题) 总的来说收获超大,这种感觉就像大一的时候学 ...
- 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 ...
- Linux安装JDK1.7
发表此篇文章纯属本人愚钝,希望以后再安装JDK不要走那么多曲折的路,也希望可以给后人借鉴. 1.以下以JDK1.7为例 具体官网地址:http://www.oracle.com/technetwork ...
- ABP中使用OAuth2(Resource Owner Password Credentials Grant模式)
ABP目前的认证方式有两种,一种是基于Cookie的登录认证,一种是基于token的登录认证.使用Cookie的认证方式一般在PC端用得比较多,使用token的认证方式一般在移动端用得比较多.ABP自 ...