Android属性动画工作原理
来源:互联网 发布:银行 数据挖掘 编辑:程序博客网 时间:2024/05/01 07:42
Android提供了属性动画这个强大的框架来帮助开发者实现比较复杂的动画。
先引出几个关键的类:ObjectAnimator,ValueAnimator,PropertyValuesHolder和Choreographer。我们从ObjectAnimator的使用方式开始分析:
ObjectAnimator.ofInt(mButton, width, 500).setDuration(5000).start()。先看ofint方法,其实就是返回了一个确定了作用对象,作用属性和属性数值的objectanimator对象。执行这个动画是start开始的。ObjectAnimator直接调用的父类ValueAnimator的super.start()。
最后调用了animationHandler.start();这里的animationHandler不是一个handler而是一个Runnable,重点来了,看这个AnimationHandler中有一个mChoreographer,这个是控制器哦。Start之后调用的是
这个方法什么作用呢?它是向choreographer中post了一个回调,并将自己传过去了,好了,剩下的工作就在choreographer中进行,先来看看这个类的结构,重要的东西有一个threadlocal的instance,看来是一个单例模式,并且应该是能支持到多线程操作的。(PS后面会有针对当前是否为UI thread的判断处理)还有FrameHandler(有handler肯定就有looper啊,没错这里也是有的,这个handler主要就是用来处理一些msg的,以及控制执行此帧的时间间隔)和一个mCallbackQueues(很明显是一个callback的队列)。
继续来看看postcallback以后的工作,会调到下面这个方法。
前面分支判断之后最终都会调用到下面这个方法,而在这个方法里面就一个难点,scheduleVsyncLocked(),这个东西是android 4.1之后加入的关键技术vsync timing和triple buffer,用于提高UI的流畅度。这个就是显示技术上的优化,不影响动画过程的分析。我们继续看普通的情况,特就是向handler中send了一个msg_do_frame的消息。
现在回过头去看看animationHandler的run中做了一些什么事情,
这一段代码很多,前面都是一些判断,有延时的就绪的结束的等等,但是重点是anim.doAnimationFrame(frameTime)和最后判断启动下一帧的过程。下一帧就是重复之前分析的过程。下面来看看doAnimationFrame这是执行设置属性变化的地方。doAnimationFrame最终会调用到animateValue,这里面的堆栈调用一目了然就不详细说了,这个方法在valueanimator中有一个,但是在子类objectanimator中重写了这个方法并同时执行了super的方法。放在一起看比较清楚。
mValues这个是父类中的成员变量,它就是之前设置的属性值的容器PropertyValuesHolder,mValues[i].calculateValue(fraction);这个方法中是根据最终属性值和进度计算当前属性值的过程(里面还会涉及到一个Interpolator插值器,这个东西一控制你当前动画执行的效果),mValues[i].setAnimatedValue(mTarget);这个显然就是使得属性值在target对象上生效的过程咯。后面的就不详细说了,其实就是使用放射机制对目标对象指向set属性和get属性(所以啊,这里对目标对象执行对应属性的变化必须确保改变的属性有set和get方法,并且与你设想的效果要是一致的,具体的使用可以看看文章开头推荐的那个帖子 )。
整个属性动画的执行过程就是如此,没有什么难的,但是给大家引出了一个难点,就是vsync timing和triple buffer的显示机制,这个可以参考 http://blog.csdn.net/innost/article/details/8272867 这个帖子的分析,说得很透彻。
网上已经有很多帖子为大家介绍属性动画的使用,先给大家推荐一篇写的比较好的。http://
www.2cto.com/kf/201401/270169.html
本文不讲使用方法,是从源码的角度来分析属性动画框架的工作原理。先引出几个关键的类:ObjectAnimator,ValueAnimator,PropertyValuesHolder和Choreographer。我们从ObjectAnimator的使用方式开始分析:
ObjectAnimator.ofInt(mButton, width, 500).setDuration(5000).start()。先看ofint方法,其实就是返回了一个确定了作用对象,作用属性和属性数值的objectanimator对象。执行这个动画是start开始的。ObjectAnimator直接调用的父类ValueAnimator的super.start()。
private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mPlayingBackwards = playBackwards; mCurrentIteration = 0; mPlayingState = STOPPED; mStarted = true; mStartedDelay = false; AnimationHandler animationHandler = getOrCreateAnimationHandler(); animationHandler.mPendingAnimations.add(this); if (mStartDelay == 0) { // This sets the initial value of the animation, prior to actually starting it running setCurrentPlayTime(0); mPlayingState = STOPPED; mRunning = true; notifyStartListeners(); } animationHandler.start();}
最后调用了animationHandler.start();这里的animationHandler不是一个handler而是一个Runnable,重点来了,看这个AnimationHandler中有一个mChoreographer,这个是控制器哦。Start之后调用的是
private void scheduleAnimation() { if (!mAnimationScheduled) { mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); mAnimationScheduled = true; }}
这个方法什么作用呢?它是向choreographer中post了一个回调,并将自己传过去了,好了,剩下的工作就在choreographer中进行,先来看看这个类的结构,重要的东西有一个threadlocal的instance,看来是一个单例模式,并且应该是能支持到多线程操作的。(PS后面会有针对当前是否为UI thread的判断处理)还有FrameHandler(有handler肯定就有looper啊,没错这里也是有的,这个handler主要就是用来处理一些msg的,以及控制执行此帧的时间间隔)和一个mCallbackQueues(很明显是一个callback的队列)。
继续来看看postcallback以后的工作,会调到下面这个方法。
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { if (DEBUG) { Log.d(TAG, "PostCallback: type=" callbackType ", action=" action ", token=" token ", delayMillis=" delayMillis); } synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now delayMillis; mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); //这里就可以看到了,当valueanimator设置回调时都会保存在这个队列当中,等待需要处理时再从里面取出来 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); //执行do frame是有间隔的哟,有一个默认的间隔是常量DEFAULT_FRAME_DELAY = 10 } } }
前面分支判断之后最终都会调用到下面这个方法,而在这个方法里面就一个难点,scheduleVsyncLocked(),这个东西是android 4.1之后加入的关键技术vsync timing和triple buffer,用于提高UI的流畅度。这个就是显示技术上的优化,不影响动画过程的分析。我们继续看普通的情况,特就是向handler中send了一个msg_do_frame的消息。
private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; if (USE_VSYNC) { if (DEBUG) { 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 / NANOS_PER_MS sFrameDelay, now); if (DEBUG) { Log.d(TAG, "Scheduling next frame in " (nextFrameTime - now) " ms."); } Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); } } } 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; } } } void doFrame(long frameTimeNanos, int frame) {doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);} 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 = SystemClock.uptimeMillis(); callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now); if (callbacks == null) { return; } mCallbacksRunning = true; } try { for (CallbackRecord c = callbacks; c != null; c = c.next) { if (DEBUG) { Log.d(TAG, "RunCallback: type=" callbackType ", action=" c.action ", token=" c.token ", latencyMillis=" (SystemClock.uptimeMillis() - c.dueTime)); } c.run(frameTimeNanos);//执行到这里终于转到正题上来了,执行了前面animationHandler的run方法。 } } finally { synchronized (mLock) { mCallbacksRunning = false; do { final CallbackRecord next = callbacks.next; recycleCallbackLocked(callbacks); callbacks = next; } while (callbacks != null); } } }
现在回过头去看看animationHandler的run中做了一些什么事情,
public void run() { mAnimationScheduled = false; doAnimationFrame(mChoreographer.getFrameTime()); } private void doAnimationFrame(long 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 there are still active or delayed animations, schedule a future call to // onAnimate to process the next frame of the animations. if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) { scheduleAnimation(); } }
这一段代码很多,前面都是一些判断,有延时的就绪的结束的等等,但是重点是anim.doAnimationFrame(frameTime)和最后判断启动下一帧的过程。下一帧就是重复之前分析的过程。下面来看看doAnimationFrame这是执行设置属性变化的地方。doAnimationFrame最终会调用到animateValue,这里面的堆栈调用一目了然就不详细说了,这个方法在valueanimator中有一个,但是在子类objectanimator中重写了这个方法并同时执行了super的方法。放在一起看比较清楚。
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); } } } void animateValue(float fraction) {//执行的就是上述操作 super.animateValue(fraction); int numValues = mValues.length; for (int i = 0; i < numValues; i) { mValues[i].setAnimatedValue(mTarget); } }
mValues这个是父类中的成员变量,它就是之前设置的属性值的容器PropertyValuesHolder,mValues[i].calculateValue(fraction);这个方法中是根据最终属性值和进度计算当前属性值的过程(里面还会涉及到一个Interpolator插值器,这个东西一控制你当前动画执行的效果),mValues[i].setAnimatedValue(mTarget);这个显然就是使得属性值在target对象上生效的过程咯。后面的就不详细说了,其实就是使用放射机制对目标对象指向set属性和get属性(所以啊,这里对目标对象执行对应属性的变化必须确保改变的属性有set和get方法,并且与你设想的效果要是一致的,具体的使用可以看看文章开头推荐的那个帖子 )。
整个属性动画的执行过程就是如此,没有什么难的,但是给大家引出了一个难点,就是vsync timing和triple buffer的显示机制,这个可以参考 http://blog.csdn.net/innost/article/details/8272867 这个帖子的分析,说得很透彻。
0 0
- Android属性动画工作原理
- 属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- 动画四 Android源码分析—属性动画的工作原理
- android动画详解二 属性动画原理
- Android属性工作原理解析
- 动画(四)属性动画的工作原理
- 动画(六)属性动画的工作原理
- Android 属性动画浅谈(二)基础运用及工作原理
- Android 属性动画的原理分析
- android:layout_weight属性的工作原理
- 百度地图开发 随着鼠标移动在状态栏显示鼠标中心的坐标
- eclipse 导入别人安卓项目报错
- Android动态设置控件的宽度和高度
- 点阵字体显示系列之三:使用ncurses显示汉字
- System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x800AC472
- Android属性动画工作原理
- TCHAR下的各种函数转换列表
- 关于PendingIntent
- Javascript整除运算
- timesten 11.2.2 操作系统核心参数配置
- Effective Java提升Code Coverage代码涵盖率 - 就是爱Java
- A. Escape from Stones
- ubutun下卸载mysql
- VC项目调试基础