This article has authorized WeChat official account. guolin_blog ( Guo Lin ) Exclusive release

I want to sort it out this time View Animation is also called mending animation (ScaleAnimation, AlphaAnimation,
TranslationAnimation...) Process analysis of these animations . Content doesn't analyze how animation works , such as Matrix
What are the principles of this kind , Because I haven't figured it out yet . This article mainly analyzes when calling View.startAnimation() after , What is the running process of animation from the beginning to the end ?


The best way to read the source code is to go with problems , This is more purposeful and targeted , It can prevent deviation and corner drilling when reading the source code , So let's start with a few questions .

The expansion of animation is very high , The system just encapsulates a few basic animations for us : translation , rotate , transparency , Zoom, etc , Interested can see the source code of these animations , They are all inherited from
Animation class , And then it's done applyTransformation() method , In this way Transformation and Matrix
Realize all kinds of cool animation , therefore , If you want to make cool animation , These still need to be understood .

I haven't figured it out yet , Limited capacity , So it is a priority to analyze the running process of animation .

First look Animation Basic usage of animation :

We're going to use one View When animating , Usually first new An animation , Then configure various parameters , Finally, call the animation to the role. View Of
startAnimation(), Pass in the animation instance as a parameter , Then you can see the effect of animation running .

that , Here comes the question :

Q1: I don't know if you've thought about it , When called View.startAnimation() after , Is the animation going to be executed soon ?

Q2: If the animation duration 300ms, When called View.startAniamtion() after , Another interface refresh operation was initiated , Then the refresh of the interface is in 300ms
After that, the animation is executed , Or in the process of animation execution, the interface refresh operation is executed ?

We all know ,applyTransformation() This is where the animation works , When this method is called back, the parameter will pass in the current animation progress (0.0 ———
1.0). It's like drawing a curve in math , The more points you give, the smoother the curve you draw , Also, when this method is called back more than once , The smoother the animation .

Like a man from 0 Zoom in to 1280 Of View Zoom in on the animation , If this process, this method only calls back 3 Next time , So the span will be very large each time , such as 0 —— 600 ——
1280, So the animation will look very abrupt ; contrary , If the method calls back dozens of times during this process , So each span may be 100, So the animation looks smooth .

I believe we've all been here applyTransformation() Log in to see the current animation progress , Sometimes there are dozens of logs , Sometimes there are dozens .

So here comes our question :

Q3:applyTransformation() What determines the number of callbacks for this method ?

okay , This article is mainly about these three questions , If you understand these three questions , I will know how to analyze when I encounter cartoon cartoon , We've located where we lost the frame , Finding the problem of lost frames is not far from solving the problem .

Source code analysis

ps: The source code of this analysis is all based on android-25
edition . Screenshots are used for the following source code , At the top of each diagram is the class name + Method name , When we want to go through it by ourselves , If you don't know which class the method belongs to, you can view it at the top of each diagram .


At the beginning of source code analysis, it may not be clear where to start , Suggestions can come from where we use it startAnimation():

Not many codes , Four methods called , So follow in one by one , First of all setStartTime() :

So it's just assigning values to some variables , No logic to run animation , Keep looking setAnimation():

View There's a Animation Member variable of type , So this method is actually to new Of ScaleAnimation Animation follows View
It's just tied , There's no logic to run the animation , Keep looking down invalidateParentCached():

invalidateParentCaches() It's easier , to mPrivateFlags Added a flag bit , Although I don't know why , But you can take a look , because
mPrivateFlags This variable follows View
Related source code often encountered , So if you can figure it out, you can figure it out , But it doesn't seem to have much to do with finding out when animation actually starts , Skip first , Follow up invalidate():

therefore invalidate() It's called internally ViewGroup Of invalidateChild(), Follow up :

Here's a do{}while() Cycle operation of , On the first cycle parent yes this, Namely ViewGroup itself , So the next step is to call
ViewGroup Own invalidateChildInParent() method , Then the loop terminates on the condition that patent ==
null, So we can guess that this method should return ViewGroup Of parent, Follow up :

So the key is PFLAG_DRAWN and PFLAG_DRAWING_CACHE_VALID When are these two assigned to
mPrivateFlags, Because as long as there is one of the two signs , The method will return mParent, The specific assignment is not clear , But what's certain is that when the animation is executed , It's satisfaction if
Conditional , That is, this method will return mParent.

A concrete View Of mParent yes ViewGroup,ViewGroup Of mParent It's also ViewGoup, So in
do{}while() It's going to keep searching in the cycle mParent, And one View The top of the tree mParent yes ViewRootImpl, So in the end, it will come
ViewRootImpl Of invalidateChildInParent() Inside .

As for an interface View Why is the top of the tree ViewRootImpl, This is just like Activity It's about the startup process . We all know , stay onCreate in
setContentView() When , Is to add our own layout file to the DecorView One for the root layout ViewGroup in , in other words
DevorView actually is View Tree root layout , Then why View The top of the tree is actually ViewRootImpl What about ?

This is because onResume() After execution ,WindowManager Will be executed addView(), And then I'll create a ViewRootImpl
object , And then DecorView Follow ViewRootImpl Object binding , And will DecorView Of mParent Set to
ViewRootImpl, and ViewRootImpl It's done ViewParent Interface , So although ViewRootImpl No inheritance View or
ViewGroup, But it does DecorView Of parent. This part should belong to Activity
Of the start-up process , So this article only gives the conclusion , No further analysis , If you are interested, you can search it by yourself .

So let's go back to where we're looking for animation execution , We're here ViewRootImpl Of invalidateChildInParent() Inside , See what it does :

First and foremost , All of its return values are null, So the previous one do{}while() The loop will stop when it's finally executed here . Then the parameters dirty At the beginning
View Of invalidateInternal() From inside , To be sure, it's not empty , Neither isEmpty, So keep following
invalidateRectOnScreen() Look in the way :

Just follow me here ,scheduleTraversals() The role is to performTraversals() Package to a Runnable inside , And throw it
Choreographer In the queue to be executed , These to be implemented Runnable Will be in the latest 16.6 ms Screen refresh signal is executed when it arrives . and
performTraversals() yes View Three operations of : measure , layout , The originator of drawing .

View No matter which one is in the tree View Layout request initiated , Draw request , All of them will eventually arrive ViewRootImpl Inside
scheduleTraversals(), And then pass it when the latest screen refresh signal arrives ViewRootImpl Of performTraversals()
From root layout DecorView Start traversing one by one View
Tree to perform measurement , layout , Three operations of drawing . That's why it is always required that the page layout level should not be too deep , Because every page refresh will arrive first ViewRootImpl
in , And then we can traverse to the specific changed View To perform the corresponding layout or drawing operations .

These should belong to Android Screen refresh mechanism , Here we will only give the conclusion , I will post another blog in a few days .

therefore , From View.startAnimation() In the process of following up source code analysis , It can also be seen that , Perform animation , In fact, it will be called internally View Redraw request operation for
invalidate() , So it will eventually arrive ViewRootImpl Of scheduleTraversals(), And then traverse when the next screen refresh signal arrives
View Tree refresh screen .

therefore , The conclusion we can get here is :

When called View.startAniamtion() after , The animation was not executed immediately , This method only does some variable initialization , And then View and
Animation Bind it , Then call redraw request operation. , Looking inside mParent, At last ViewRootImpl Of scheduleTraversals
Start a traversal in View Tree request , This request will be executed when the latest screen refresh signal arrives , call performTraversals From root layout DecorView
Start traversal View tree .

Where animation really works

that , Come here , We can guess , In fact, the real execution of animation should be in ViewRootImpl Traversal initiated View In the process of tree . measure , layout , draw ,View
The three basic operations displayed on the screen are ViewRootImpl Of performTraversals() To control , And as View The top of the tree
parent, To control this one Veiw Three basic operations of tree , You can only traverse through layers . therefore , measure , layout , Drawing the execution of three basic operations will be a traversal operation .

I'm following these three processes , Finally, we found that , When following the drawing process , See the code related to animation , So we skip the other two processes , Look directly at the drawing process :

I didn't draw this picture , I found it on the Internet , The drawing process starts with ViewRootImpl Initiated by , And then from DecorView Start traversal View tree . And the implementation of traversal , Yes
View#draw() In the method . Let's take a look at the comments on this method :

In this method, the above six things are mainly done , In general, if View Need to draw , I'll call my own onDraw(), And then if there's a son View, Will call
dispatchDraw() Notify children of drawing events View.ViewGroup Rewritten dispatchDraw(), Called drawChild(), and
drawChild() Called child View Of draw(Canvas, ViewGroup, long), And this method will call draw(Canvas)
method , So it's traversal . The whole process is just like the picture above .

In this process , When it comes draw(Canvas, ViewGroup, long) Lishi , Found code related to animation :

Remember we called View.startAnimation(Animation) It will come in Animation Assign to mCurrentAnimation
Are you ready .

So it came in Animation , Now it's out , So where animation really works is in applyLegacyAnimation() In the way ( This method is available in
android-22 Versions and earlier are named drawAnimation)

Now let's make sure where the animation really starts , You see it all onAnimationStart() 了 , We also see the initialization of animation , And called Animation Of
getTransformation, This method is the core of animation , Come in and have a look :

Several things have been done in this method :

* Record the time of the first frame of the animation

* Calculate the progress of the animation based on the time between the current time and the first frame of the animation and the duration of the animation

* Control the animation progress to 0-1 between , exceed 1 Indicates that the animation is over , Reassign to 1 that will do

* Calculate the actual progress of the animation according to the interpolator

* call applyTransformation() Apply animation
therefore , We've been able to make sure applyTransformation() When was the callback , When does animation really start . that Q1 It's done ,Q2
I can sort it out . Because we know ,applyTransformation() In the process of drawing draw()
Implemented in the process , So obviously when the screen refresh signal of each frame comes , ergodic View Trees are used to recalculate screen data , It's called View
Refresh of , And animation is only executed in the process .

The next step is Q3 了, We know applyTransformation()
Where animation works , When this method is called back again and again , Parameters will pass in the progress of the animation , So the rendering effect is that the animation is running according to the progress .

however , Let's start from scratch , Found where the animation really works , eureka applyTransformation() Where called , But none of these places have been seen for
perhaps while Circulation , Once View Traversal drawing operation of tree , Animation can only be executed once ? So how did it get called back so many times ?

We know applyTransformation() Yes getTransformation() Called in , And this method has a boolean
Of the return value , Let's see what its return logic is :

in other words getTransformation() The return value of represents whether the animation is complete , Remember where it was called getTransformation() bar , go
applyLegacyAnimation() Let's see what we did after we got this return value :

When the animation is not finished , Will call again invalidate() method , Notice to ViewRootImpl
Initiate a traverse request again , When the next screen refresh signal comes , Pass again performTraversals() ergodic View When the tree is drawn , The View Of draw
When a notification is called , Will call again applyLegacyAnimation() Method to perform animation related operations , Include calls getTransformation()
Calculate animation progress , call applyTransformation() 应用动画.

也就是说,动画很流畅的情况下,其实是每隔 16.6ms 即每一帧到来的时候,执行一次 applyTransformation(),直到动画完成.所以这个
applyTransformation() 被回调多次是这么来的,而且这个回调次数并没有办法人为进行设定.


还记得 getTransformation() 方法在计算动画进度时是根据参数传进来的 currentTime 的么,而这个 currentTime
可以理解成是发起遍历操作这个时刻的系统时间(实际 currentTime 是在 Choreographer 的 doFrame()



首先,当调用了 View.startAnimation() 时动画并没有马上就执行,而是通过 invalidate() 层层通知到 ViewRootImpl
发起一次遍历 View 树的请求,而这次请求会等到接收到最近一帧到了的信号时才去发起遍历 View 树绘制操作.

从 DecorView 开始遍历,绘制流程在遍历时会调用到 View 的 draw() 方法,当该方法被调用时,如果 View

在 applyLegacyAnimation() 这个方法里,如果动画还没有执行过初始化,先调用动画的初始化方法 initialized(),同时调用
onAnimationStart() 通知动画开始了,然后调用 getTransformation() 来根据当前时间计算动画进度,紧接着调用
applyTransformation() 并传入动画进度来应用动画.

getTransformation() 这个方法有返回值,如果动画还没结束会返回 true,动画已经结束或者被取消了返回 false.所以
applyLegacyAnimation() 会根据 getTransformation() 的返回值来决定是否通知 ViewRootImpl
再发起一次遍历请求,返回值是 true 表示动画没结束,那么就去通知 ViewRootImpl 再次发起一次遍历请求.然后当下一帧到来时,再从
DecorView 开始遍历 View 树绘制,重复上面的步骤,这样直到动画结束.

有一点需要注意,动画是在每一帧的绘制流程里被执行,所以动画并不是单独执行的,也就是说,如果这一帧里有一些 View
需要重绘,那么这些工作同样是在这一帧里的这次遍历 View 树的过程中完成的.每一帧只会发起一次 perfromTraversals() 操作.

以上,就是本篇所有的内容,将 View 动画 Animation 的运行流程原理梳理清楚,但要搞清楚为什么动画会出现卡顿现象的话,还需要理解 Android



Q1:大伙都清楚,View 动画区别于属性动画的就是 View 动画并不会对这个 View 的属性值做修改,比如平移动画,平移之后 View

Q2:既然 View 动画不会改变 View 的属性值,那么如果是缩放动画时,View 需要重新执行测量操作么?