編輯:安卓省電與加速
通常我們說一個系統不如另一個系統流暢,說的就是前者動畫顯示不如後者流暢,因此動畫顯示流暢程度是衡量一個系統流暢性的關鍵指標。為什麼這樣說呢?這是因為流暢的動畫顯示需要60fps的UI刷新速度,然而這卻不是一個容易達到的速度。Android 5.0通過引入Render Thread盡最大努力提升動畫顯示流暢性。本文就分析Render Thread顯示動畫的過程,以便了解它是如何提高動畫顯示流暢性的。
在前面Android應用程序UI硬件加速渲染技術簡要介紹和學習計劃一文中,我們提到了Render Thread對動畫顯示的兩個優化。第一個優化是在動畫顯示期間,臨時將動畫的目標View的Layer Type設置為LAYER_TYPE_HARDWARE,這樣就可以使得目標View以Open GL裡面的Frame Buffer Object(FBO)進行渲染。這種優化的效果就如Render Thread直接以Open GL裡面的Texture來渲染TextureView一樣。第二個優化是在Main Thread不需要參與動畫的顯示過程時,動畫就會被注冊到Render Thread中,這樣動畫的計算和顯示過程就完全由Render Thread來負責。這種優化帶來的好處就是在動畫顯示期間,Main Thread可以去處理其它的用戶輸入,而且動畫的顯示也會更加流暢。
上面描述的兩種動畫優化涉及到的Main Thread和Render Thread的交互過程如圖1所示:
圖1 Main Thread與Render Thread的動畫交互模型
接下來,我們就通過代碼分析上述的兩種動畫顯示優化過程。
我們通過調用View類的成員函數animate可以獲得一個ViewPropertyAnimator對象,如下所示:
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { ...... public ViewPropertyAnimator animate() { if (mAnimator == null) { mAnimator = new ViewPropertyAnimator(this); } return mAnimator; } ...... }這個函數定義在文件frameworks/base/core/java/android/view/View.java中。
有了這個ViewPropertyAnimator對象之後,就可以調用它的成員函數withLayer將它關聯的View的Layer Type設置為LAYER_TYPE_HARDWARE,如下所示:
public class ViewPropertyAnimator { ...... public ViewPropertyAnimator withLayer() { mPendingSetupAction= new Runnable() { @Override public void run() { mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); if (mView.isAttachedToWindow()) { mView.buildLayer(); } } }; final int currentLayerType = mView.getLayerType(); mPendingCleanupAction = new Runnable() { @Override public void run() { mView.setLayerType(currentLayerType, null); } }; ...... return this; } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ViewPropertyAnimator.java中。
ViewPropertyAnimator類的成員函數withLayer創建了兩個Runable,分別保存在成員變量mPendingSetupAction和mPendingCleanupAction中。其中,成員變量mPendingSetupAction指向的Runable在動畫開始顯示之前執行,而成員變量mPendingCleanupAction指向的Runable在動畫結束顯示之後執行。由此我們就可以看到:
1. 在動畫開始顯示之前,目標View的Layer Type會被設置為LAYER_TYPE_HARDWARE,並且它的成員函數buildLayer會被調用來創建一個Layer。
2. 在動畫結束顯示之後,目標View的Layer Type會被恢復為它之前的Layer Type。注意,這裡調用目標View的成員函數getLayerType獲得的是它的Layer Type未被設置為LAYER_TYPE_HARDWARE的Layer Type。
接下來我們就繼續分析View類的成員函數buildLayer的實現,以便可以了解為一個View設置一個Layer的過程。
View類的成員函數buildLayer的實現如下所示:
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { ...... public void buildLayer() { ...... final AttachInfo attachInfo = mAttachInfo; ...... switch (mLayerType) { case LAYER_TYPE_HARDWARE: updateDisplayListIfDirty(); if (attachInfo.mHardwareRenderer != null && mRenderNode.isValid()) { attachInfo.mHardwareRenderer.buildLayer(mRenderNode); } break; case LAYER_TYPE_SOFTWARE: buildDrawingCache(true); break; } } ...... }
這個函數定義在文件frameworks/base/core/java/android/view/View.java中。
前面已經將當前正在處理的View的Layer Type設置為LAYER_TYPE_HARDWARE,因此View類的成員函數buildLayer首先是調用我們在前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文中分析過的View類的另外一個成員函數updateDisplayListIfDirty更新它的Display List。更新完畢之後,再調用保存在成員變量mAttachInfo描述的一個AttachInfo對象的成員變量mHardwareRenderer指向的一個ThreadedRenderer對象的成員函數buildLayer為當前正在處理的View創建一個Layer。
從這裡還可以看到,如果當前正在處理的View的Layer Type被設置為LAYER_TYPE_SOFTWARE,即該View是以軟件方式進行渲染的,那麼就會調用另外一個成員函數buildDrawingCache將View上次繪制得到的UI緩存在一個Bitmap中,以便下次以快速地繪制View動畫的下一幀。View類的成員函數buildDrawingCache的實現,同樣可以參考前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文。
接下來我們主要關注為一個View創建一個Layer的過程,即ThreadedRenderer對象的成員函數buildLayer的實現,如下所示:
public class ThreadedRenderer extends HardwareRenderer { ...... @Override void buildLayer(RenderNode node) { nBuildLayer(mNativeProxy, node.getNativeDisplayList()); } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。
ThreadedRenderer對象的成員函數buildLayer調用另外一個成員函數nBuildLayer為參數node描述的一個Render Node關聯的View創建一個Layer。
ThreadedRenderer對象的成員函數nBuildLayer是一個JNI函數,由Native層的函數android_view_ThreadedRenderer_buildLayer實現,如下所示:
static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz, jlong proxyPtr, jlong nodePtr) { RenderProxy* proxy = reinterpret_cast(proxyPtr); RenderNode* node = reinterpret_cast (nodePtr); proxy->buildLayer(node); }
這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
參數proxyPtr描述的是一個RenderProxy對象,這裡調用它的成員函數buildLayer為參數nodePtr描述的一個Render Node創建一個Layer。
RenderProxy類的成員函數buildLayer的實現如下所示:
CREATE_BRIDGE2(buildLayer, CanvasContext* context, RenderNode* node) { args->context->buildLayer(args->node); return NULL; } void RenderProxy::buildLayer(RenderNode* node) { SETUP_TASK(buildLayer); args->context = mContext; args->node = node; postAndWait(task); }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp中。
RenderProxy類的成員函數buildLayer首先是通過宏SETUP_TASK創建一個Task,接下來再調用另外一個成員函數postAndWait將該Task添加到Render Thread的Task Queue去等待執行。最後這個Task由宏CREATE_BRIDGE2聲明的函數buildLayer來執行,主要就是調用參數context描述的一個CanvasContext對象的成員函數buildLayer為參數node描述的一個Render Node創建一個Layer。
CanvasContext類的成員函數buildLayer的實現如下所示:
void CanvasContext::buildLayer(RenderNode* node) { ...... TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState()); ...... node->prepareTree(info); ...... mCanvas->flushLayerUpdates(); ...... }這個函數定義在文件frameworks/base/libs/hwui/renderthred/CanvasContext.cpp中。
這裡就可以看到CanvasContext類的成員函數buildLayer調用了我們在前面Android應用程序UI硬件加速渲染的Display List渲染過程分析一文的關鍵函數——RenderNode類的成員函數prepareTree,它用來將參數node描述的Render Node的Display List從Main Thread同步到Render Thread中,並且為該Render Node創建了一個Layer,但是這個Layer處理待更新狀態。
CanvasContext類的成員函數buildLayer接下來繼續調用成員變量mCanvas指向的一個OpenGLRenderer對象的成員函數flushLayerUpdates更新剛才創建的Layer,它的實現如下所示:
void OpenGLRenderer::flushLayerUpdates() { ...... updateLayers(); flushLayers(); ...... }這個函數定義在文件frameworks/base/libs/hwui/OpenGLRenderer.cpp中。
OpenGLRenderer類的成員函數flushLayerUpdates主要是執行了以下兩個操作:
1. 調用成員函數updateLayers重排和合並所有設置了Layer的Render Node的Display List的繪制命令,這些Layer包括了我們在前面一步創建的Layer。
2. 調用成員函數flushLayers執行所有所有設置了Layer的經過了重排和合並的Render Node的Display List的繪制命令,使得每一個這樣的Render Node的UI都分別渲染在一個FBO上。
關於OpenGLRenderer類的成員函數updateLayers和flushLayers的實現,可以參考前面前面Android應用程序UI硬件加速渲染的Display List渲染過程分析一文。
這樣,我們就臨時地為一個即將要顯示動畫的View創建了一個Layer,這個Layer將即將要顯示的動畫的View的UI渲染在一個FBO,這樣以後就可以基於這個FBO來更高效率地顯示動畫了。
後面的動畫顯示過程實質上就不斷地渲染應用程序窗口的UI,這個過程可以參考前面Android應用程序UI硬件加速渲染的Display List渲染過程分析一文。不過,再結合前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文,我們可以知道,在這個過程中,並不是應用程序窗口視圖中的每一個View的Display List都是需要重建的,而且對於要顯示動畫的View,我們也只是需要將動畫參數應用在前面為它創建的FBO之上即可。當然,為了得到應用程序窗口的UI,在渲染的過程中,需要重新執行一遍應用程序窗口視圖中的每一個View的Display List的繪制命令。我們可以將這個過程看作是應用程序窗口的各個View的UI合成過程。相對於應用程序窗口的每一個View的Display List構建,以及對它裡面的繪制命令進行重排和合並的過程來說,上述合成過程的成本是低很多的。
以上分析的就是View動畫顯示過程中的第一種優化,即在View的動畫開始顯示之前,臨時地為它創建一個Layer,使得View的UI渲染在一個FBO上,以後的動畫就直接作用在該FBO上。接下來我們繼續分析View動畫顯示過程的第二種優化,即將View動畫的計算和顯示完全交給Render Thread來負責。
當我們通過View類的成員函數animate獲得了一個即將要顯示動畫的View關聯的ViewPropertyAnimator對象之後,就可以通過這個ViewPropertyAnimator對象設置各種動畫參數。動畫參數設置完畢,就可以調用上述ViewPropertyAnimator對象的成員函數start進行動畫顯示。
ViewPropertyAnimator類的成員函數start的實現如下所示:
public class ViewPropertyAnimator { ...... public void start() { ...... startAnimation(); } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ViewPropertyAnimator.java中。
ViewPropertyAnimator類的成員函數start調用了另外一個成員函數startAnimation來啟動動畫,後者的實現如下所示:
public class ViewPropertyAnimator { ...... /** * A RenderThread-driven backend that may intercept startAnimation */ private ViewPropertyAnimatorRT mRTBackend; ...... private void startAnimation() { if (mRTBackend != null && mRTBackend.startAnimation(this)) { return; } ...... ValueAnimator animator = ValueAnimator.ofFloat(1.0f); ...... animator.start(); } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ViewPropertyAnimator.java中。
ViewPropertyAnimator類的成員函數startAnimation首先是檢查成員變量mRTBackend的值是否等於null。如果不等於null,那麼它指向的就是一個ViewPropertyAnimatorRT對象,因此接下來就調用該ViewPropertyAnimatorRT對象的成員函數startAnimation來執行動畫相關的操作。
如果調用ViewPropertyAnimator類的成員變量mRTBackend指向的ViewPropertyAnimatorRT對象的成員函數startAnimation得到的返回值為true,就表示由Render Thread完全負責動畫的計算以及顯示。否則的話,就需要由Main Thread負責動畫的計算,然後將計算好的動畫應用在View上,再由Render Thread負責將動畫顯示出來。
從ViewPropertyAnimator類的成員變量mRTBackend的注釋我們也可以看到,ViewPropertyAnimatorRT類是用來攔截ViewPropertyAnimator類負責的動畫的,也就是將動畫完全交給Render Thread來管理。不過在5.0的代碼中,還沒有看到任何初始化ViewPropertyAnimator類的成員變量mRTBackend的代碼。這就是意味著ViewPropertyAnimator類的成員變量mRTBackend始終為null,因此就不會去調用ViewPropertyAnimatorRT類的成員函數startAnimation。
我們相信以後ViewPropertyAnimatorRT類的功能一定會派上用場的,因此接下來我們就假設ViewPropertyAnimator類的成員變量mRTBackend已經被始化,這樣我們就可以更好地理解我們上面提到的動畫顯示的第二種優化。
ViewPropertyAnimatorRT類的成員函數startAnimation的實現如下所示:
class ViewPropertyAnimatorRT { ...... public boolean startAnimation(ViewPropertyAnimator parent) { ...... if (!canHandleAnimator(parent)) { return false; } doStartAnimation(parent); return true; } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ViewPropertyAnimatorRT.java中。
ViewPropertyAnimatorRT類的成員函數startAnimation首先是調用成員函數canHandleAnimator判斷是否能夠讓Render Thread來完全負責當前正在處理的動畫。如果不能完全負責,那麼就返回一個false值給調用者。否則的話,就調用另外一個成員函數doStartAnimation將當前正在處理的動畫交給Render Thread來管理。
我們先看什麼情況下ViewPropertyAnimatorRT類的成員函數startAnimation不能讓Render Thread來完全負責當前正在處理的動畫,也就是ViewPropertyAnimatorRT類的成員函數canHandleAnimator的實現,如下所示:
class ViewPropertyAnimatorRT { ...... private boolean canHandleAnimator(ViewPropertyAnimator parent) { ...... if (parent.getUpdateListener() != null) { return false; } if (parent.getListener() != null) { // TODO support return false; } if (!mView.isHardwareAccelerated()) { // TODO handle this maybe? return false; } if (parent.hasActions()) { return false; } // Here goes nothing... return true; } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ViewPropertyAnimatorRT.java中。
參數parent指向的是一個ViewPropertyAniamtor對象,該ViewPropertyAniamtor對象原先是由它負責顯示View的動畫的,現在ViewPropertyAnimatorRT類的成員函數canHandleAnimator判斷它是否設置了一系列的Listener。如果設置了,就意味著Main Thread需要獲得動畫顯示過程中的通知,這樣就不能將一個動畫完全地將交給Render Thread來管理。
上面提到的Listener有兩個:
1. 用來監聽動畫更新事件的Listener。這個Listener如果設置有,那麼就可以通過調用ViewPropertyAniamtor類的成員函數getUpdateListener獲得。
2. 用來監聽動畫的開始和結束等事件的Listener。這個Listener如果設置有,那麼就可以通過調用ViewPropertyAniamtor類的成員函數getListener獲得。
此外,如我們前面所述,如果在View的動畫顯示之前,我們調用了與它關聯的ViewPropertyAniamtor對象的成員函數withLayer,那麼與它關聯的ViewPropertyAniamtor對象就會在內部創建兩個Runnable。這兩個Runable用來臨時地將要顯示動畫的View的Layer Type設置為LAYER_TYPE_HARDWARE。在這種情況下,調用與View關聯的ViewPropertyAniamtor對象的成員函數hasActions得到的返回值就等於true。這意味著Main Thread需要獲得動畫顯示開始和結束的通知,因此就不能將一個動畫完全地將交給Render Thread來管理。
再者,如果要顯示動畫的View不支持硬件加速渲染,即調用它的成員函數isHardwareAccelerated得到的返回值等於false。很明顯,Render Thread是通過硬件加速渲染的方式來顯示View的動畫的。因此,在這種情況下,也不能一個動畫完全地將交給Render Thread來管理。
我們假設ViewPropertyAnimatorRT類的成員函數canHandleAnimator的返回值為false,那麼接下來就會調用到ViewPropertyAnimatorRT類的成員函數doStartAnimation,它的實現如下所示:
class ViewPropertyAnimatorRT { ...... private void doStartAnimation(ViewPropertyAnimator parent) { int size = parent.mPendingAnimations.size(); ...... for (int i = 0; i < size; i++) { NameValuesHolder holder = parent.mPendingAnimations.get(i); int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant); final float finalValue = holder.mFromValue + holder.mDeltaValue; RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue); animator.setStartDelay(startDelay); animator.setDuration(duration); animator.setInterpolator(interpolator); animator.setTarget(mView); animator.start(); mAnimators[property] = animator; } parent.mPendingAnimations.clear(); } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ViewPropertyAnimatorRT.java中。
當前要顯示的動畫保存在參數parent描述的一個ViewPropertyAnimator對象的成員變量mPendingAnimations描述的一個NameValueHodler對象列表中。ViewPropertyAnimatorRT類的成員函數doStartAnimation為這個列表的每一個NameValueHodler對象都創建一個RenderNodeAnimator對象,並且將相關的動畫參數都設置到新創建的RenderNodeAnimator對象中去。當這些參數設置完畢,就可以調用新創建的RenderNodeAnimator對象的成員函數start,來它們標記為可以執行的狀態。
其中,上述新創建的RenderNodeAnimator對象有一個重要的參數需要設置,就是它所關聯的View,這是通過調用RenderNodeAnimator類的成員函數setTarget來完成的。RenderNodeAnimator類的成員函數setTarget在執行的過程中,就會將相關的動畫注冊到Render Thread中去,以便Render Thread可以執行它們。
RenderNodeAnimator類的成員函數setTarget的實現如下所示:
public class RenderNodeAnimator extends Animator { ...... public void setTarget(View view) { mViewTarget = view; setTarget(mViewTarget.mRenderNode); } ...... }這個函數定義在文件frameworks/base/core/java/android/view/RenderNodeAnimator.java中。
參數view描述的就是要顯示動畫的View,RenderNodeAnimator類的成員函數setTarget首先是將它保存成員變量mTargetView中,接首再獲得與它關聯的一個Render Node傳遞給另外一個重載版本的成員函數setTarget處理,如下所示:
public class RenderNodeAnimator extends Animator { ...... private void setTarget(RenderNode node) { ...... mTarget = node; mTarget.addAnimator(this); } ...... }這個函數定義在文件frameworks/base/core/java/android/view/RenderNodeAnimator.java中。
RenderNodeAnimator類的重載版本的成員函數setTarget首先是將參數node描述的一個Render Node保存在成員變量mTarget中,接著再通過調用RenderNode類的成員函數addAniamtor將當前正在處理的動畫注冊到Render Thread中去。
RenderNode類的成員函數addAniamtor的實現如下所示:
public class RenderNode { ...... public void addAnimator(RenderNodeAnimator animator) { ...... nAddAnimator(mNativeRenderNode, animator.getNativeAnimator()); mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this); } ...... }這個函數定義在文件frameworks/base/core/java/android/view/RenderNode.java中。
RenderNode類的成員函數addAniamtor首先是調用另外一個成員函數nAddAnimator將參數animator描述的動畫注冊到Render Thread中去。
RenderNode類的成員變量mOwningView描述的是一個與當前正在處理的Render Node關聯的一個View,這個View就是要顯示動畫的View。每一個View都有一個成員變量mAttachInfo,它指向的是一個AttachInfo對象,該AttachInfo對象描述的是一個View所屬的窗口的相關信息。其中,這個AttachInfo對象有一個成員變量mViewRootImpl,它指向的是一個ViewRootImpl對象,該ViewRootImpl對象負責管理一個View所屬的窗口,例如用來分發輸入事件給窗口,以及用來發起窗口的繪制流程等。
RenderNode類的成員函數addAniamtor接下來要做的事情就是調用上述的ViewRootImpl對象的成員函數registerAnimatingRenderNode告訴Render Thread,當前正在處理的Render Node有動畫注冊到了它裡面,這樣Render Node就可以將渲染應用窗口的下一幀時,顯示前面已經注冊的動畫。
接下來我們就先分析RenderNode類的成員函數nAddAnimator的實現,接著再分析ViewRootImpl類的成員函數registerAnimatingRenderNode的實現。
RenderNode類的成員函數nAddAnimator是一個JNI函數,由Native層的函數android_view_RenderNode_addAnimator實現,如下所示:
static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz, jlong renderNodePtr, jlong animatorPtr) { RenderNode* renderNode = reinterpret_cast這個函數定義在文件frameworks/base/core/jni/android_view_RenderNode.cpp中。(renderNodePtr); RenderPropertyAnimator* animator = reinterpret_cast (animatorPtr); renderNode->addAnimator(animator); }
參數renderNodePtr指向的是Native層的一個RenderNode對象,該RenderNode對象與要顯示動畫的View關聯。參數animatorPtr指向的Native層的一個RenderPropertyAnimator對象,該RenderPropertyAnimator對象描述的就是要顯示的動畫。這裡調用RenderNode類的成員函數addAnimator將參數animatorPtr描述的動畫保存在參數renderNodePtr描述的Render Node的內部。
RenderNode類的成員函數addAnimator的實現如下所示:
void RenderNode::addAnimator(const sp& animator) { mAnimatorManager.addAnimator(animator); }
RenderNode類的成員變量mAnimatorManager描述的是一個AnimatorManager對象。這個AnimatorManager對象用來管理一個RenderNode對象所關聯的動畫。因此,RenderNode類的成員函數addAnimator所做的事情就是將參數animator描述的動畫保存在當前正在處理的RenderNode對象內部的一個AnimatorManager對象中,這是通過調用AnimatorManager類的成員函數addAnimator完成的。
AnimatorManager類的成員函數addAnimator的實現如下所示:
void AnimatorManager::addAnimator(const sp& animator) { animator->incStrong(0); animator->attach(&mParent); mNewAnimators.push_back(animator.get()); }
在分析AnimatorManager類的成員函數addAnimator的實現之前,我們首先了解AnimatorManager類的三個成員變量:
1. mParent,它描述的是一個RenderNode對象,該RenderNode對象與當前正在處理的AnimatorManager對象關聯。
2. mNewAnimators,它描述的是一個Vector,該Vector用來保存新增加的動畫。
3. mAnimators,它描述的也是一個Vector。在繪制應用程序窗口的下一幀之前,新增加的動畫會從成員變量mNewAnimators描述的Vector轉移到成員變量mAnimators描述的Vector中去等待處理。
AnimatorManager類的成員函數addAnimator首先是增加參數animator描述的一個BaseRenderNodeAnimator對象的引用計數,因為後面要將它添加成員變量mNewAnimators描述的一個Vector中去。此外,AnimatorManager類的成員函數addAnimator還會調用參數animator描述的一個BaseRenderNodeAnimator對象的成員函數attach將該BaseRenderNodeAnimator對象與要顯示動畫的Render Node進行關聯。
這一步執行完成之後,我們就將要顯示的動畫設置到目標View關聯的一個RenderNode對象內部的一個AnimatorManager對象中去了。返回到Java層的RenderNode類的成員函數addAniamtor中,接下來我們繼續分析ViewRootImpl類的成員函數registerAnimatingRenderNode的實現,以便可以了解Render Thread知道有Render Node有新的動畫需要顯示的過程。
ViewRootImpl類的成員函數registerAnimatingRenderNode的實現如下所示:
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { ...... public void registerAnimatingRenderNode(RenderNode animator) { if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.registerAnimatingRenderNode(animator); } else { if (mAttachInfo.mPendingAnimatingRenderNodes == null) { mAttachInfo.mPendingAnimatingRenderNodes = new ArrayList這個函數定義在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。(); } mAttachInfo.mPendingAnimatingRenderNodes.add(animator); } } ...... }
當ViewRootImpl類的成員變量mAttachInfo指向的一個AttachInfo對象的成員變量mHardwareRenderer的值不等於null的時候,它指向的是一個ThreadedRenderer對象。在這種情況下,ViewRootImpl類的成員函數registerAnimatingRenderNode會直接調用該ThreadedRenderer對象的成員函數registerAnimatingRenderNode將參數animator描述的一個Render Node注冊到Render Thread中去,以便Render Thread知道哪些Rendr Node有新的動畫需要顯示。
另一方面,當ViewRootImpl類的成員變量mAttachInfo指向的一個AttachInfo對象的成員變量mHardwareRenderer的值等於null的時候,這意味著應用程序窗口使用軟件渲染或者使用硬件加速渲染但是硬件加速渲染環境還沒有初始化好。在這兩種情況下,都是先將參數animator描述的Render Node保存在成員變量mAttachInfo指向的一個AttachInfo對象的成員變量mPendingAnimatingRenderNodes描述的一個列表中等待處理。
從前面Android應用程序UI硬件加速渲染的Display List構建過程分析一文可以知道,當使用硬件加速渲染時,應用程序窗口的渲染是從調用ThreadedRenderer類的成員函數draw開始的,它的實現如下所示:
public class ThreadedRenderer extends HardwareRenderer { ...... @Override void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) { ...... updateRootDisplayList(view, callbacks); ...... if (attachInfo.mPendingAnimatingRenderNodes != null) { final int count = attachInfo.mPendingAnimatingRenderNodes.size(); for (int i = 0; i < count; i++) { registerAnimatingRenderNode( attachInfo.mPendingAnimatingRenderNodes.get(i)); } attachInfo.mPendingAnimatingRenderNodes.clear(); // We don't need this anymore as subsequent calls to // ViewRootImpl#attachRenderNodeAnimator will go directly to us. attachInfo.mPendingAnimatingRenderNodes = null; } int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos, recordDuration, view.getResources().getDisplayMetrics().density); if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) { attachInfo.mViewRootImpl.invalidate(); } } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。
ThreadedRenderer類的成員函數draw在調用成員函數updateRootDisplayList構建應用程序窗口的Display List之後,並且調用成員函數nSyncAndDrawFrame渲染應用程序窗口的Display List之前,會調用成員函數registerAnimatingRenderNode處理保存在上面提到的AttachInfo對象的成員變量mPendingAnimatingRenderNodes描述的一個列表中的每一個Render Node。
ThreadedRenderer類的成員函數registerAnimatingRenderNode的實現如下所示:
public class ThreadedRenderer extends HardwareRenderer { ...... @Override void registerAnimatingRenderNode(RenderNode animator) { nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); } ...... }這個函數定義在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。
ThreadedRenderer類的成員函數registerAnimatingRenderNode調用另外一個成員函數nRegisterAnimatingRenderNode將參數animator描述的一個Render Node注冊到Render Thread中去。
ThreadedRenderer類的成員函數nRegisterAnimatingRenderNode是一個JNI函數,由Native層的函數android_view_ThreadedRenderer_registerAnimatingRenderNode實現,如下所示:
static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz, jlong rootNodePtr, jlong animatingNodePtr) { RootRenderNode* rootRenderNode = reinterpret_cast這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。(rootNodePtr); RenderNode* animatingNode = reinterpret_cast (animatingNodePtr); rootRenderNode->attachAnimatingNode(animatingNode); }
參數rootNodePtr指向的是一個RootRenderNode對象,該RootRenderNode對象描述的是應用程序窗口的Root Render Node,這裡主要就是將參數animatorNodePtr描述的一個Render Node注冊在上述的RootRenderNode對象的內部,這是通過調用RootRenderNode類的成員函數attachAnimatingNode實現的。
RootRenderNode類的成員函數attachAnimatingNode的實現如下所示:
class RootRenderNode : public RenderNode, ErrorHandler { public: ...... void attachAnimatingNode(RenderNode* animatingNode) { mPendingAnimatingRenderNodes.push_back(animatingNode); } ...... private: ...... std::vector< sp這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。> mPendingAnimatingRenderNodes; };
RootRenderNode類的成員函數attachAnimatingNode將參數animatingNode描述的一個Render Node保存成員變量mPendingAnimatingRenderNodes描述的一個Vector中。保在這個Vector中的Render Node將會在應用程序窗口的下一幀被渲染時得到處理。
從前面Android應用程序UI硬件加速渲染的Display List渲染過程分析一文可以知道,Render Thread在渲染應用程序窗口的下一幀時,會調用CanvasContext類的成員函數prepareTree將應用程序窗口的Display List從Main Thread同步到Render Thread,如下所示:
void CanvasContext::prepareTree(TreeInfo& info) { ...... info.renderer = mCanvas; ...... mAnimationContext->startFrame(info.mode); mRootRenderNode->prepareTree(info); mAnimationContext->runRemainingAnimations(info); ...... int runningBehind = 0; // TODO: This query is moderately expensive, investigate adding some sort // of fast-path based off when we last called eglSwapBuffers() as well as // last vsync time. Or something. mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); info.out.canDrawThisFrame = !runningBehind; if (info.out.hasAnimations || !info.out.canDrawThisFrame) { if (!info.out.requiresUiRedraw) { // If animationsNeedsRedraw is set don't bother posting for an RT anim // as we will just end up fighting the UI thread. mRenderThread.postFrameCallback(this); } } }這個函數定義定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
CanvasContext類的成員函數prepareTree的詳細實現分析可以參考前面Android應用程序UI硬件加速渲染的Display List渲染過程分析一文,這裡我們主要關注與動畫相關的兩個操作:
1. 第一個操作是在調用RenderNode類的成員函數prepareTree同步應用程序窗口的Display List之前,調用成員變量mAnimationContext描述的一個AnimationContext對象的成員函數startFrame執行一些動畫顯示的准備工作。
2. 第二個操作是在調用RenderNode類的成員函數prepareTree同步應用程序窗口的Display List之後,調用成員變量mAnimationContext描述的一個AnimationContext對象的成員函數runRemainingAnimations更新剩下的還未完成的動畫。
接下來我們就分別分析這兩個操作的執行過程,即分析AnimationContext類的成員函數startFrame和runRemainingAnimations的實現。不過,在分析這兩個函數的實現之前,我們首先要了解CanvasContext類的成員變量mAnimationContext的初始化過程。
從前面Android應用程序UI硬件加速渲染環境初始化過程分析一文可以知道,在Android應用程序的硬件加速渲染環境的初始化過程中,會創建一個RenderProxy對象,如下所示:
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, jboolean translucent, jlong rootRenderNodePtr) { RootRenderNode* rootRenderNode = reinterpret_cast這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。(rootRenderNodePtr); ContextFactoryImpl factory(rootRenderNode); return (jlong) new RenderProxy(translucent, rootRenderNode, &factory); }
在調用RenderProxy類的構造函數創建一個RenderProxy對象的時候,傳遞進去的第三個參數是一個ContextFactoryImpl對象。
我們繼續看RenderProxy類的構造函數的實現,如下所示:
CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) { return new CanvasContext(*args->thread, args->translucent, args->rootRenderNode, args->contextFactory); } RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) : mRenderThread(RenderThread::getInstance()) , mContext(0) { SETUP_TASK(createContext); args->translucent = translucent; args->rootRenderNode = rootRenderNode; args->thread = &mRenderThread; args->contextFactory = contextFactory; mContext = (CanvasContext*) postAndWait(task); mDrawFrameTask.setContext(&mRenderThread, mContext); }這個函數定義在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp中。
RenderProxy類的構造函數首先是向Render Thread的Task Queue發送一個Task,並且等待該Task執行完成。上述Task在執行的時候,會調用由宏CREATE_BRIDGE4聲明的函數createContext。該函數所做的事情就是創建一個CanvasContext對象。該CanvasContext對象最終保存在RenderProxy類的成員變量mContext中。
在調用CanvasContext類的構造函數創建一個CanvasContext對象的時候,傳遞進去的第四個參數就是在前面分析的函數android_view_ThreadedRenderer_createProxy聲明的一個ContextFactoryImpl對象。
CanvasContext類的構造函數的實現如下所示:
CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) :...... { mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord()); ...... }這個函數定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
從這裡就可以看出,CanvasContext的成員變量mAnimationContext指向的一個AnimationContext對象是由參數contextFactory描述的一個IContextFactory接口的成員函數createAnimationContext創建的。
從前面的調用過程可以知道,參數contextFactory指向的實際上是一個ContextFactoryImpl對象,它的成員函數createAnimationContext的實現如下所示:
class ContextFactoryImpl : public IContextFactory { public: ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {} virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { return new AnimationContextBridge(clock, mRootNode); } private: RootRenderNode* mRootNode; };這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
ContextFactoryImpl類的成員函數createAnimationContext創建的是一個AnimationContextBridge對象,由此就可見,CanvasContext的成員變量mAnimationContext實際指向的一個AnimationContextBridge對象。
明白了這一點之後,回到前面分析的CanvasContext類的成員函數prepareTree中,它在同步應用程序窗口的Display List之前,調用了AnimationContextBridge類的成員函數startFrame執行一些動畫顯示的准備工作。
AnimationContextBridge類的成員函數startFrame的實現如下所示:
class AnimationContextBridge : public AnimationContext { public: ...... // Marks the start of a frame, which will update the frame time and move all // next frame animations into the current frame virtual void startFrame(TreeInfo::TraversalMode mode) { if (mode == TreeInfo::MODE_FULL) { mRootNode->doAttachAnimatingNodes(this); } AnimationContext::startFrame(mode); } ...... private: sp這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。mRootNode; ...... };
參數mode的值等於TreeInfo::MODE_FULL,表示目前Render Thread是處於同步應用程序窗口的Display List的過程中。在這種情況下,AnimationContextBridge類的成員函數startFrame執行兩個操作:
1. 調用成員變量mRootNode描述的一個RootRenderNode對象的成員函數doAttachAnimatingNodes處理它內部保存的有動畫顯示的Render Node。
2. 調用父類AnimationContext的成員函數startFrame繼續執行一些動畫顯示之前的准備工作。
接下來,我們就先分析RootRenderNode類的成員函數doAttachAnimatingNodes的實現,再分析AnimationContext類的成員函數startFrame的實現。
RootRenderNode類的成員函數doAttachAnimatingNodes的實現如下所示:
class RootRenderNode : public RenderNode, ErrorHandler { public: ...... void doAttachAnimatingNodes(AnimationContext* context) { for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) { RenderNode* node = mPendingAnimatingRenderNodes[i].get(); context->addAnimatingRenderNode(*node); } mPendingAnimatingRenderNodes.clear(); } private: ...... std::vector< sp這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。> mPendingAnimatingRenderNodes; };
RootRenderNode類的成員函數doAttachAnimatingNodes遍歷保存在成員變量mPendingAnimatingRenderNodes描述的一個Vector中的每一個RenderNode對象,並且分別調用參數context描述的一個AnimationContextBridge對象的成員函數addAnimatingRenderNode對它們進行處理。
AnimationContextBridge類的成員函數addAnimatingRenderNode是從父類AnimationContext繼承下來的,它的實現如下所示:
void AnimationContext::addAnimatingRenderNode(RenderNode& node) { if (!node.animators().hasAnimationHandle()) { AnimationHandle* handle = new AnimationHandle(node, *this); addAnimationHandle(handle); } }這個函數定義在文件frameworks/base/libs/hwui/AnimationContext.cpp中。
參數node指向的是一個RenderNode對象,調用它的成員函數animators可以獲得它內部的一個AnimatorManager對象。如果該AnimatorManager對象還沒有設置過一個AnimationHandle對象,那麼AnimationContext類的成員函數addAnimatingRenderNode就為其設置一個AnimationHandle對象,並且這個AnimationHandle對象會通過AnimationContext類的成員函數addAnimationHandle保存在當前正在處理的一個AnimationContext對象的內部。
AnimationContext類的成員函數addAnimationHandle的實現如下所示:
void AnimationContext::addAnimationHandle(AnimationHandle* handle) { handle->insertAfter(&mNextFrameAnimations); }這個函數定義在文件frameworks/base/libs/hwui/AnimationContext.cpp中。
AnimationContext類的成員函數addAnimationHandle所做的事情就是將參數handle描述的一個AnimationHandle對象插入成員變量mNextFrameAnimations描述的一個AnimationHandle列表中。
由此我們就可以推斷出,只要AnimationContext類的成員變量NextFrameAnimations描述的一個AnimationHandle列表不為空,那麼就意味著Render Thread在渲染應用程序窗口的下一幀的時候,有Render Node需要顯示動畫。
這一步執先完成之後,返回到AnimationContextBridge類的成員函數startFrame中,接下來它所做的事情就是調用父類AnimationContext的成員函數startFrame繼續執行一些動畫顯示之前的准備工作。
AnimationContext的成員函數startFrame的實現如下所示:
void AnimationContext::startFrame(TreeInfo::TraversalMode mode) { ...... AnimationHandle* head = mNextFrameAnimations.mNextHandle; if (head) { mNextFrameAnimations.mNextHandle = NULL; mCurrentFrameAnimations.mNextHandle = head; head->mPreviousHandle = &mCurrentFrameAnimations; } ...... }這個函數定義在文件frameworks/base/libs/hwui/AnimationContext.cpp中。
AnimationContext的成員函數startFrame所做的事情就是將成員變量mNextFrameAnimations描述的一個AnimationHandle列表轉移到另外一個成員變量mCurrentFrameAnimations中。
由此我們就可以推斷出,只要AnimationContext類的成員變量mCurrentFrameAnimations描述的一個AnimationHandle列表不為空,那麼就意味著Render Thread在渲染應用程序窗口的當前幀的時候,有Render Node需要顯示動畫。
這一步執行完成之後,返回到CanvasContext類的成員函數prepareTree中,當它同步同步應用程序窗口的Display List之後,就調用成員變量mAnimationContext描述的一個AnimationContextBridge對象的成員函數runRemainingAnimations更新剩下的還未完成的動畫。
AnimationContextBridge類的成員函數runRemainingAnimations的實現如下所示:
class AnimationContextBridge : public AnimationContext { public: ...... // Runs any animations still left in mCurrentFrameAnimations virtual void runRemainingAnimations(TreeInfo& info) { AnimationContext::runRemainingAnimations(info); ...... } ...... };這個函數定義在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
AnimationContextBridge類的成員函數runRemainingAnimations主要是通過調用父類AnimationContext的成員函數runRemainingAnimations更新剩下的還未完成的動畫。
AnimationContext類的成員函數runRemainingAnimations的實現如下所示:
void AnimationContext::runRemainingAnimations(TreeInfo& info) { while (mCurrentFrameAnimations.mNextHandle) { AnimationHandle* current = mCurrentFrameAnimations.mNextHandle; AnimatorManager& animators = current->mRenderNode->animators(); animators.pushStaging(); animators.animateNoDamage(info); ...... } }這個函數定義在文件frameworks/base/libs/hwui/AnimationContext.cpp中。
前面提到,應用程序窗口當前幀要顯示動畫都記錄在AnimationContext類的成員mCurrentFrameAnimations描述的一個AnimationHandle列表中。因此,AnimationContext類的成員函數runRemainingAnimations就遍歷這個列表中的每一個AnimationHandle,並且獲得與其關聯的一個AnimatorManager。有了這些AnimatorManager之的,就可以調用它們的成員函數pushStaging和animateNoDamage來執行動畫,其實就是計算動畫的下一幀參數,以便應用在目標Render Node上。
AnimatorManager類的成員函數pushStaging的實現如下所示:
void AnimatorManager::pushStaging() { if (mNewAnimators.size()) { ...... move_all(mNewAnimators, mAnimators); } for (vector::iterator it = mAnimators.begin(); it != mAnimators.end(); it++) { (*it)->pushStaging(mAnimationHandle->context()); } }
從前面的分析可以知道,一開始的時候,應用程序窗口新增加的動畫都保存在AnimatorManager類的成員變量mNewAnimators描述的一個Vector中,這裡將它們移動至AnimatorManager類的另外一個成員變量mAnimators中,類似於我們在前面Android應用程序UI硬件加速渲染的Display List渲染過程分析一文提到的將Display List從Main Thread同步到Render Thread一樣。
最後,AnimatorManager類的成員函數pushStaging遍歷已經移動至新列表中的每一個動畫,並且調用它們的成員函數pushStaging,用來同步它們的開始和結束狀態,也就是檢查動畫是否已經被start或者已經end了。如果已經被start,那麼就執行一些動畫開始執行前的准備工作,例如計算動畫的開始時間。如果已經end,就發出事件通知給偵聽者。
這一步執先完成之的,回到前面分析的AnimationContext類的成員函數runRemainingAnimations中,接下來就調用AniamtorManager類的成員函數animateNoDamage執行動畫,它的實現如下所示:
void AnimatorManager::animateNoDamage(TreeInfo& info) { if (!mAnimators.size()) return; animateCommon(info); }這個函數定義在文件frameworks/base/libs/hwui/AnimatorManager.cpp中。
AniamtorManager類的成員函數animateNoDamage通過調用另外一個成員函數animateCommon來執行當前正在處理的AnimatorManager對象關聯的動畫,也就是某一個Render Node關聯的動畫。
AniamtorManager類的成員函數animateCommon的實現如下所示:
uint32_t AnimatorManager::animateCommon(TreeInfo& info) { AnimateFunctor functor(info, mAnimationHandle->context()); std::vector< BaseRenderNodeAnimator* >::iterator newEnd; newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor); mAnimators.erase(newEnd, mAnimators.end()); mAnimationHandle->notifyAnimationsRan(); return functor.dirtyMask; }這個函數定義在文件frameworks/base/libs/hwui/AnimatorManager.cpp中。
前面提到,當前正在處理的AnimatorManager對象關聯的動畫都保存其成員變量mAnimators描述的一個Vector,因此這裡就會通過函數std::remove_if來遍歷這些動畫,並且對於每一個動畫,都通過本地變量functor描述的一個AnimatorFunctor對象的操作符重載函數()來執行它的當前幀。
AnimatorFunctor類的操作符重載函數()執行完成動畫的當前幀之後,如果動畫已經完成,即沒有下一幀了,那麼它就會返回一個true值給調用者,這樣會導致函數std::remove_if將該動畫移動至列表的末尾位置。
AnimatorFunctor類的操作符重載函數()的實現如下所示:
class AnimateFunctor { public: AnimateFunctor(TreeInfo& info, AnimationContext& context) : dirtyMask(0), mInfo(info), mContext(context) {} bool operator() (BaseRenderNodeAnimator* animator) { dirtyMask |= animator->dirtyMask(); bool remove = animator->animate(mContext); if (remove) { animator->decStrong(0); } else { if (animator->isRunning()) { mInfo.out.hasAnimations = true; } if (CC_UNLIKELY(!animator->mayRunAsync())) { mInfo.out.requiresUiRedraw = true; } } return remove; } uint32_t dirtyMask; private: TreeInfo& mInfo; AnimationContext& mContext; };這個函數定義在文件frameworks/base/libs/hwui/AnimatorManager.cpp中。
AnimatorFunctor類的操作符重載函數()主要就是調用參數animator指向的一個BaseRenderNodeAnimator對象的成員函數animate來執行該BaseRenderNodeAnimator對象所描述的動畫的當前幀。
當BaseRenderNodeAnimator類的成員函數animate的返回值等於true的時候,就表示參數animator描述的動畫已經顯示結束了,這意味著它可以從我們上面描述的動畫列表中刪除。
另一方面,當BaseRenderNodeAnimator類的成員函數animate的返回值等於false的時候,就表示參數animator描述的動畫還沒有顯示完成。這時候如果該動畫沒有被中途停止,即調用參數animator指向的一個BaseRenderNodeAnimator對象的成員函數isRunning的返回值等於true,那麼就會將當前正在處理的AnimatorFunctor對象的成員變量mInfo指向的一個TreeInfo對象的成員變量out描述的一個Out結構體的成員變量hasAnimations的值設置為true。這樣做的作用是什麼呢?
我們首先梳理一下Render Thread目前所處的位置。目前Render Thread正處於將Main Thread的Display List同步到Render Thread的過程中,只不過目前正在處理的動畫相關的操作。Render Thead在執行這個同步過程之前,會構造一個TreeInfo對象,這一點可以參考前面Android應用程序UI硬件加速渲染的Display List渲染過程分析一文分析的DrawFrameTask類的成員函數run。DrawFrameTask類的成員函數run會將該TreeInfo對象傳遞給接下來要調用的函數。AnimatorFunctor類的操作符重載函數()就是被調用的其中一個函數,並且它的成員變量mInfo指向的TreeInfo對象就上述在DrawFrameTask類的成員函數run構造的TreeInfo對象。
通過將這個TreeInfo對象的成員變量out描述的一個Out結構體的成員變量hasAnimations的值,就可以讓Render Thread通知應用程序窗口的下一幀是否還有動畫需要顯示。再結合上述TreeInfo對象的成員變量out描述的一個Out結構體的另外一個成員變量requiresUiRedraw的值,Render Thread就可以知道它是自動執行那些未完成的動畫,還是等待Main Thread通知它執行。
當參數animator指向的一個BaseRenderNodeAnimator對象描述的是一個同步動畫時,那麼調用它的成員函數mayRunAsync的返回值就等於false,這時候上述TreeInfo對象的成員變量out描述的一個Out結構體的成員變量requiresUiRedraw的值就會被設置為true,這將導致未完成的動畫要由Main Thread通知Render Thread來執行。這個邏輯同時也意味著只有當所有未完成的動畫都是異步動畫時,Render Thread才可以自動執行它們。這個自動執行未完成的異步動畫的過程我們後面再詳細分析。
這一步執行完成之後,返回到前面分析的AniamtorManager類的成員函數animateCommon,也就是函數std::remove_if執行完畢的位置。函數std::remove_if執行完畢,會返回一個iterator。這個iterator指向的是被移動至列表末尾的第一個動畫。這意味著從這個iterator開始,到列表的結束,保存的動畫都是已經執行完成了的,因此就可以將它們從列表中刪除。
最後,AniamtorManager類的成員函數animateCommon調用成員變量mAnimationHandle指向的一個AnimationHandle對象的成員函數notifyAnimationsRan,用來發送一個動畫幀執先完成的通知。從上面的分析可以知道,這個AnimationHandle對象是在添加一個新的動畫添到Render Thread時創建並且設置給關聯的AnimatorManager對象的,具體可以參數前面分析的AnimationContext類的成員函數addAnimatingRenderNode的實現。
AnimationHandle類的成員函數notifyAnimationsRan的實現如下所示:
void AnimationHandle::notifyAnimationsRan() { removeFromList(); if (mRenderNode->animators().hasAnimators()) { mContext.addAnimationHandle(this); } else { release(); } }這個函數定義在文件frameworks/base/libs/hwui/AnimationContext.cpp中。
AnimationHandle類的成員函數notifyAnimationsRan首先是調用成員函數removeFromList將當前正處理的一個AnimationHandle對象從所在的列表中移除。回憶前面的分析,當前正處理的一個AnimationHandle對象位於的列表正是AnimationContext類的成員變量mCurrentFrameAnimations描述的一個AnimationHandle列表。
將當前正處理的一個AnimationHandle對象從所在的列表中移除之後,AnimationHandle類的成員函數notifyAnimationsRan再判斷它所關聯的Render Node是否還有未執行完成的動畫。如果有的話,那麼就會調用成員變量mContext描述的一個AnimationContext對象的成員函數addAnimationHandle將當前正處理的一個AnimationHandle對象重新添加到另外一個列表去。
從前面的分析可以知道,AnimationContext類的成員函數addAnimationHandle會將指定的AnimationContext對象添加到其成員變量mNextFrameAnimations描述的一個列表中。又從前面分析的AnimationContext類的成員函數startFrame可以知道,保存在AnimationContext類的成員變量mNextFrameAnimations描述的列表中的AnimationContext對象在應用程序窗口動畫顯示之前,會被移動至AnimationContext類的成員變量mCurrentFrameAnimations描述的另一個列表中。
這意味動畫的執行過程如下所示:
1. 在應用程序窗口每一幀的渲染過程中,如果需要顯示一個動畫,那麼就需要先將一個AnimationContext對象添加到AnimationContext類的成員變量mNextFrameAnimations描述的列表中。
2. 當一個動畫需要執行一幀時,與它關聯的AnimationContext對象將會AnimationContext類的成員變量mNextFrameAnimations描述的列表轉移至成員變量mCurrentFrameAnimations描述的另外一個列表。
3. 當一個動畫的一幀執行完成時,與它關聯的AnimationContext對象又會從AnimationContext類的成員變量mCurrentFrameAnimations描述的列表移除。
4. 當一個動畫的一幀執行完成時,如果整個動畫還未執行完成,那麼它關聯的AnimationContext對象又會重新添加到AnimationContext類的成員變量mNextFrameAnimations描述的列表中。接著下來又重復執行前面的第1步。
另一方面,如果一個Render Node關聯的所有動畫均已執行完畢,那麼AnimationHandle類的成員函數notifyAnimationsRan會調用另外一個成員函數release將它內部的一個AnimatorManager對象關聯的一個AnimationHandle對象移除。這意味著下次我們再為該Render Node時,需要重新為它創建和關聯一個新的AnimationHandle對象,如前面分析的AnimationContext類的成員函數addAnimatingRenderNode所示。
這樣在應用程序窗口每一個幀的渲染過程中涉及到的動畫的執行過程我們就分析完畢。注意,這個動畫執行過程是在Main Thread和Render Thread完全同步的條件下執行的,並且這裡說的動畫執行,只是計算出動畫對目標Render Node的屬性影響。例如,計算出動畫對目標Render Node在X軸和Y軸的位置。新計算出來的屬性將會在目標Render Node的Display List的繪制命令轉化為Open GL命令執行時得到應用,從而就完成一個動畫幀的顯示。
理解了動畫的正常執行過程之後,也就是由Main Thread驅動Render Thread執行的過程,我們再來看Render Thread在不需要Main Thread干預的情況下自動執行動畫的過程。在分析之前,我們首先理解一下前面提到的同步動畫和異步動畫的概念。
從前面的分析可以知道,如果我們使用Render Thread來計算和執行動畫,那麼動畫就會被封裝成一個RenderNodeAnimator對象,如前面分析的ViewPropertyAnimatorRT類的成員函數doStartAnimation所示。
RenderNodeAnimator類有一個成員函數setAllowRunningAsynchronously,允許在一個動畫執行之前,設置為是否可以異步執行,如下所示:
public class RenderNodeAnimator extends Animator { ...... @Override public void setAllowRunningAsynchronously(boolean mayRunAsync) { checkMutable(); nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync); } ...... }這個函數定義在文件frameworks/base/core/java/android/view/RenderNodeAnimator.java中。
當參數mayRunAsync的值等於true時,就表示將一個動畫設置為異步動畫;否則的話,就設置為同步動畫。這個信息會通過調用RenderNodeAnimator類的另外一個成員函數nSetAllowRunningAsync同步到Native層關聯的同樣是用來描述動畫的一個BaseRenderNodeAnimator對象中去。
RenderNodeAnimator類的成員函數nSetAllowRunningAsync是一個JNI函數,由Native層的函數setAllowRunningAsync實現,如下所示:
static void setAllowRunningAsync(JNIEnv* env, jobject clazz, jlong animatorPtr, jboolean mayRunAsync) { BaseRenderNodeAnimator* animator = reinterpret_cast(animatorPtr); animator->setAllowRunningAsync(mayRunAsync); }
參數animatorPtr描述的就是Native用來描述動畫的一個BaseRenderNodeAnimator對象,這裡調用它的成員函數setAllowRunningAsync設置它是一個同步動畫還是一個異步動畫。
BaseRenderNodeAnimator類的setAllowRunningAsync的實現如下所示:
class BaseRenderNodeAnimator : public VirtualLightRefBase { ...... public: ...... ANDROID_API void setAllowRunningAsync(bool mayRunAsync) { mMayRunAsync = mayRunAsync; } bool mayRunAsync() { return mMayRunAsync; } ...... };這兩個函數定義在文件frameworks/base/libs/hwui/Animator.h中。
BaseRenderNodeAnimator類的setAllowRunningAsync將動畫的同步異步信息記錄在成員變量mMayRunAsync中。這樣就可以通過調用另外一個成員函數mayRunAsync就可以知道一個動畫是同步的還是異步,如前面分析的AnimatorFunctor類的操作符重載函數()所示。
了解了一個動畫的同步異步概念之後,回到前面分析的CanvasContext類的成員函數prepareTree中,當它同步完成應用程序窗口的Display List以及處理過應用程序窗口的動畫之後,如果應用程序窗口還有未完成的動畫,並且這些都是異步動畫,那麼就會注冊一個IFrameCallback接口到Render Thread中。為了方便描述,我們重新將這部分代碼列出來,如下所示:
void CanvasContext::prepareTree(TreeInfo& info) { ...... info.renderer = mCanvas; ...... mAnimationContext->startFrame(info.mode); mRootRenderNode->prepareTree(info); mAnimationContext->runRemainingAnimations(info); ...... if (info.out.hasAnimations || !info.out.canDrawThisFrame) { if (!info.out.requiresUiRedraw) { // If animationsNeedsRedraw is set don't bother posting for an RT anim // as we will just end up fighting the UI thread. mRenderThread.postFrameCallback(this); } } }這個函數定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
根據我們前面的分析,當應用程序窗口還有未完成的動畫時,參數info指向的一個TreeInfo對象的成員變量out指向的一個Out結構體的成員變量hasAnimations的值就等於true,而當這些未完成的動畫都是異步動畫時,上述的Out結構體的成員變量requiresUiRedraw的值就等於false。在這種情況下,CanvasContext類的成員函數prepareTree就會注冊一個IFrameCallback接口到Render Thread中。這個注冊過程可以參考前面Android應用程序UI硬件加速渲染環境初始化過程分析一文。
上面注冊的IFrameCallback接口CanvasContext類實現,從前面Android應用程序UI硬件加速渲染環境初始化過程分析一文可以知道,當下一個Vsync信號到來的時候,Render Thread就會調用CanvasContext類的成員函數doFrame。
CanvasContext類的成員函數doFrame的實現如下所示:
void CanvasContext::doFrame() { ...... TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); prepareTree(info); if (info.out.canDrawThisFrame) { draw(); } }這個函數定義在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
CanvasContext類的成員函數doFrame首先是構造一個mode值等於TreeInfo::MODE_RT_ONLY的TreeInfo對象,接著再使用這個TreeInfo對象來調用我們前面分析過的成員函數prepareTree。從前面Android應用程序UI硬件加速渲染的Display List渲染過程分析一文可以知道,當傳遞給anvasContext類的成員函數prepareTree的TreeInfo對象的mode值等於TreeInfo::MODE_RT_ONLY時,它就僅僅會執行動畫相關的操作,以及更新設置了Layer的Render Node的操作,而不會執行同步應用程序窗口的Display List的操作。
執行完成動畫相關的操作之後,CanvasContext類的成員函數doFrame接下來在允許繪制應用程序窗口下一幀的條件下,也就是在Surface Flinger不是太忙的時候,就會調用另外一個成員函數draw渲染應用程序窗口的Display List,也就是使用Open GL命令來渲染應用程序窗口的UI,並且將得到的圖形緩沖區提交給Surface Flinger進行合成以及顯示在屏幕上。
注意,前面在調用CanvasContext類的成員函數prepareTree的過程中,如果那些未完成的異步動畫還未執行完成,那麼它又會繼續向Render Thread注冊一個IFrameCallback接口,這樣就會使得下一個Vsync信號到來的時候,CanvasContext類的成員函數doFrame又會被調用。這個過程直至所有的異步動畫都執行完成為止。這樣就使得Render Thread可以在沒有Main Thread干預的條件下自動執行動畫。
這樣,Android在動畫顯示過程中的兩個優化點都分析完成了,從中我們就可以看到Render Thread對提高動畫流暢性的貢獻。至些,Android應用程序UI硬件加速渲染技術這個系列的文章我們就全部學習完成了。要重新學習,請參考前面Android應用程序UI硬件加速渲染技術簡要介紹和學習計劃一文。更多的信息也可以關注老羅的新浪微博:http://weibo.com/shengyangluo。
關於紅米手機耗電這個問題,眾口不一,如果你覺得自己的紅米耗電嚴重的話,那有可能是沒有將手機設置好哦~接下來就來告訴大家紅米手機耗電怎麼辦,該如何正確設置。 要解決紅米耗
ROM介紹: ROM大小:76.3 MB 版本:2.3.7 UI類型:其它 一、加入了最新的安智市場 V3.3 免費下載海量軟件! 二、加入RE管理器,方便管理軟件
智能手機的耗電量一直讓人焦慮,特別是三星推出的note系列大屏手機,耗電更是驚人,這樣就有了省電模式。今天小編要為
ROM介紹: <刷機注意事項> 1、刷機前請確保電池電量保持在60%以上,如果因為沒電導致刷機失敗,只能拆電池出來充電! 2、請先確定你的手機版本再來刷機