Android动画执行过程源码分析

本文详细分析了Android中View.startAnimation的执行流程,从View开始,经过ViewGroup,再到ViewRootImpl,深入探讨了动画如何影响视图的绘制过程,包括Choreographer在其中的作用以及如何调度动画帧。通过对源码的解读,揭示了动画执行过程中的关键步骤,如drawAnimation、invalidateChildInParent等,帮助理解Android动画机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、View.startAnimation
    public void startAnimation(Animation animation) {
        animation.setStartTime(Animation.START_ON_FIRST_FRAME);
        setAnimation(animation);
        invalidateParentCaches();
        invalidate(true);
    }

setAnimation 将mCurrentAnimation设置为当前要执行的animation
 
 
 
invalidateParentCaches将当前view的mParent的flag设为 PFLAG_INVALIDATED
然后调用invalidate,同时参数为true,使当前view的缓存失效

2、View.invalidate
    // Propagate the damage rectangle to the parent view.
    final AttachInfo ai = mAttachInfo;
    final ViewParent p = mParent;
    if (p != null && ai != null && l < r && t < b) {
        final Rect damage = ai.mTmpInvalRect;
        damage.set(l, t, r, b);
        p.invalidateChild(this, damage);
    }
这里会将失效的矩阵传递给ViewParent,ViewParent的直接子类有ViewGroup和ViewRootImpl,通常情况下,view的直接parent是ViewGroup,所以接下来会进入ViewGroup.invalidateChild
3、ViewGroup.invalidateChild
在这个方法中
            final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
                    == PFLAG_DRAW_ANIMATION;
            do {
                View view = null;
                if (parent instanceof View) {
                    view = (View) parent;
                }
                ...
                if (drawAnimation) {
                    if (view != null) {
                        view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                    } else if (parent instanceof ViewRootImpl) {
                        ((ViewRootImpl) parent).mIsAnimating = true;
                    }
                }
                // ...
                parent = parent.invalidateChildInParent(location, dirty);
            } while (parent != null);
这里会循环调用parent的invalidateChildInParent方法,在ViewGroup.invalidateChildInParent中,会返回它的mParent,但是最下层的ViewRootImpl.invalidateChildInParent方法返回null,因为它没有父view,所以循环终止。drawAnimation在第一次判断时,并不为true,因为这个PFLAG_DRAW_ANIMATION flag只有当执行了一次绘制后才会被设置,参见后面View.drawAnimation。当动画执行过程中的invalidateChildParent时,drawAnimation会为true,这时下面的while循环就会给ViewGroup设置PFLAG_DRAW_ANIMATION flag,给ViewRootImpl.mIsAnimating设置为true。
4、ViewRootImpl.invalidateChildInParent
        final boolean intersected = localDirty.intersect(0, 0,
                (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
        if (!intersected) {
            localDirty.setEmpty();
        }
        if (!mWillDrawSoon && (intersected || mIsAnimating)) {
            scheduleTraversals();
        }
intersected 这个变量,顾名思义是指矩阵是否相交,用来判断需要失效的矩阵是否和当前ViewRootImpl的矩阵相交,如果相交的话就执行下面的scheduleTraversals.同时如果是在动画执行过程中,mIsAnimating变量也为true,也能走到scheduleTraversals这一步。
5、ViewRootImpl.scheduleTraversals
mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
这里主要就是调用了Choreographer的postCallBack,mTraversalRunnable 里面就是调用了View measure,layout,draw的非常经典的performTraversals方法。然后我们再看看Choreographer这个类
6、Choreographer.postCallback
            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);
            }
addCallbackLocked这个方法会将当前Runnable按执行时间先后进行排队(所以如果在UI线程中最很多事情的话,肯定会导致动画的掉帧).
7、Choreographer.scheduleFrameLocked
            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 / TimeUtils.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);
            }
USE_VSYNC,判断这个VSync(VSync是Android 4.1以后引入的新的view绘制技术,可以参见 http://blog.chinaunix.net/uid-26669815-id-3272173.html)是否打开,如果打开,走VSync的定时刷新逻辑,VSync刷新时间由VSync控制,不受Choreographer.sFrameDelay(默认为 10ms,也就是100FPS)延迟控制,如果VSync没有打开走下面的延迟刷新逻辑,会判断上次刷新绘制帧+sFrameDelay的时间和当前请求绘制的时间哪个大,选择大的最后下一帧的刷新时间,但是由于使用Handler处理的,所以不一定会在规定时间点开始执行。不管哪种方式最终都会走到Choreographer.doFrame方法。
8、Choreographer.doFrame
            mFrameScheduled = false;
            mLastFrameTimeNanos = frameTimeNanos;

        doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
        doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
        doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
将最后一次绘制帧的时间设置为当前的时间,精确到纳秒,同时调用callback.run方法。而之前CALLBACK_TRAVERSAL对应的Runnable是TraversalRunnable,其run方法调用的是ViewRootImpl.doTraversal.
9、ViewRootImpl.doTraversal
这个方法很简单,之前有介绍,就是调用了ViewRootImpl.performTraversals
10、ViewRootImpl.performTraversals
这个方法就不细说了,重点不在这,这里会进行绘制的分发,ViewRootImpl.performDraw->ViewRootImpl.draw->ViewGroup.dispatchDraw->ViewGroup.drawChild->View.draw(注意是这个:draw(Canvas canvas, ViewGroup parent, long drawingTime))。
11、ViewGroup.dispatchDraw
值得注意的是ViewGroup.drawChild和View.draw这两个方法都有boolean返回值,这个返回值是用来标志是否需要继续执行invalidate方法,当动画没有结束,那么肯定返回true。而在方法最后,会判断一个标志位是否被置,如果置了将继续调用invalidate方法。
        if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
            invalidate(true);
        }
接下来再看的View的draw方法。
12、View.draw(Canvas canvas, ViewGroup parent, long drawingTime)
这里没有太多,主要看View.drawAnimation方法
13、View.drawAnimation
这里会调用Animation.getTransformation方法,这个方法处理Animation的一些生命周期,比如Animation的start,end,repeat和applyTransformation,同时有个返回值,如果动画执行结束,返回false,没有结束就返回true。如果是true的话,就将修改parent的一些flag,比如PFLAG_DRAW_ANIMATION,参见第3步。
            if (!a.willChangeBounds()) {
                if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | ViewGroup.FLAG_ANIMATION_DONE)) == ViewGroup.FLAG_OPTIMIZE_INVALIDATE) {
                    parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED;
                } else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) {
                    // The child need to draw an animation, potentially offscreen, so
                    // make sure we do not cancel invalidate requests
                    parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                    parent.invalidate(mLeft, mTop, mRight, mBottom);
                }
            } else {
                if (parent.mInvalidateRegion == null) {
                    parent.mInvalidateRegion = new RectF();
                }
                final RectF region = parent.mInvalidateRegion;
                a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
                        invalidationTransform);

                // The child need to draw an animation, potentially offscreen, so
                // make sure we do not cancel invalidate requests
                parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;

                final int left = mLeft + (int) region.left;
                final int top = mTop + (int) region.top;
                parent.invalidate(left, top, left + (int) (region.width() + .5f),
                        top + (int) (region.height() + .5f));
            }
这里有几个分支,如果进入第一个分支,执行parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED,这个flag,会在ViewGroup.dispatchDraw方法最后有判断,如果有这个标志,继续执行View.invalidate,参见第11步。而如果进了其他几个分支的话,显而易见,直接调用了parent.invalidate方法,也就继续进入了第2步。所以只要动画没结束,都会继续调用invalidate,直到动画结束。

以上就是Android Animation的动画的完整流程了。源码参考的是Android5.0。

附1:dispatchDraw流程

附2:如果调用startAnimation,那么只有当该View和屏幕区域有交集,且可见时才会执行动画。否则不会执行,除非定时调用invalidate的方式。

同时根据以上的源码分析,可以解释一些Animation上的一些诡异情况:
(1)、通过WindowManager.addView方式添加的View是否可以直接执行(是指直接parent是ViewRootImpl的情况)基本动画(外层没有ViewGroup包裹)?
答案是无法执行,因为外层没有ViewGroup,无法走正常的Animation流程。在ViewRootImpl.draw过程中无法通过ViewGroup.dispatchDraw走View.drawAnimation过程。
(2)、为什么有的View调用了startAnimation动画不能执行?
因为有可能这个view的矩阵和ViewRootImpl的矩阵没有交集,比如View不在屏幕内,在进行第3步时,得到的矩阵交集为0。而第4步则会失败。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值