Android应用程序UI硬件加速渲染的动画执行过程分析

  通常我们说一个系统不如另一个系统流畅,说的就是前者动画显示不如后者流畅,因此动画显示流畅程度是衡量一个系统流畅性的关键指标。为什么这样说呢?这是因为流畅的动画显示需要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对象,如下所示:
 这个函数定义在文件frameworks/base/core/java/android/view/View.java中。
      有了这个ViewPropertyAnimator对象之后,就可以调用它的成员函数withLayer将它关联的View的Layer Type设置为LAYER_TYPE_HARDWARE,如下所示:
 这个函数定义在文件frameworks/base/core/java/android/view/ViewPropertyAnimator.java中。
       ViewPropertyAnimator类的成员函数withLayer创建了两个Runnable,分别保存在成员变量mPendingSetupAction和mPendingCleanupAction中。其中,成员变量mPendingSetupAction指向的Runnable在动画开始显示之前执行,而成员变量mPendingCleanupAction指向的Runnable在动画结束显示之后执行。由此我们就可以看到:
 
       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的实现如下所示:
 这个函数定义在文件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的实现,如下所示:
  这个函数定义在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。
       ThreadedRenderer对象的成员函数buildLayer调用另外一个成员函数nBuildLayer为参数node描述的一个Render Node关联的View创建一个Layer。
 
       ThreadedRenderer对象的成员函数nBuildLayer是一个JNI函数,由Native层的函数android_view_ThreadedRenderer_buildLayer实现,如下所示:
 这个函数定义在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
 
       参数proxyPtr描述的是一个RenderProxy对象,这里调用它的成员函数buildLayer为参数nodePtr描述的一个Render Node创建一个Layer。
 
       RenderProxy类的成员函数buildLayer的实现如下所示:
  这个函数定义在文件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的实现如下所示:
  这个函数定义在文件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,它的实现如下所示:
 这个函数定义在文件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的实现如下所示:
这个函数定义在文件frameworks/base/core/java/android/view/ViewPropertyAnimator.java中。
       ViewPropertyAnimator类的成员函数start调用了另外一个成员函数startAnimation来启动动画,后者的实现如下所示:
    这个函数定义在文件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的实现如下所示:
 这个函数定义在文件frameworks/base/core/java/android/view/ViewPropertyAnimatorRT.java中。
       ViewPropertyAnimatorRT类的成员函数startAnimation首先是调用成员函数canHandleAnimator判断是否能够让Render Thread来完全负责当前正在处理的动画。如果不能完全负责,那么就返回一个false值给调用者。否则的话,就调用另外一个成员函数doStartAnimation将当前正在处理的动画交给Render Thread来管理。
 
       我们先看什么情况下ViewPropertyAnimatorRT类的成员函数startAnimation不能让Render Thread来完全负责当前正在处理的动画,也就是ViewPropertyAnimatorRT类的成员函数canHandleAnimator的实现,如下所示:
这个函数定义在文件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。这两个Runnable用来临时地将要显示动画的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,它的实现如下所示:
 这个函数定义在文件frameworks/base/core/java/android/view/ViewPropertyAnimatorRT.java中。
       当前要显示的动画保存在参数parent描述的一个ViewPropertyAnimator对象的成员变量mPendingAnimations描述的一个NameValuesHodler对象列表中。ViewPropertyAnimatorRT类的成员函数doStartAnimation为这个列表的每一个NameValuesHodler对象都创建一个RenderNodeAnimator对象,并且将相关的动画参数都设置到新创建的RenderNodeAnimator对象中去。当这些参数设置完毕,就可以调用新创建的RenderNodeAnimator对象的成员函数start,来它们标记为可以执行的状态。
 
       其中,上述新创建的RenderNodeAnimator对象有一个重要的参数需要设置,就是它所关联的View,这是通过调用RenderNodeAnimator类的成员函数setTarget来完成的。RenderNodeAnimator类的成员函数setTarget在执行的过程中,就会将相关的动画注册到Render Thread中去,以便Render Thread可以执行它们。
 
       RenderNodeAnimator类的成员函数setTarget的实现如下所示:
 这个函数定义在文件frameworks/base/core/java/android/view/RenderNodeAnimator.java中。
       参数view描述的就是要显示动画的View,RenderNodeAnimator类的成员函数setTarget首先是将它保存成员变量mTargetView中,接首再获得与它关联的一个Render Node传递给另外一个重载版本的成员函数setTarget处理,如下所示:
  这个函数定义在文件frameworks/base/core/java/android/view/RenderNodeAnimator.java中。
       RenderNodeAnimator类的重载版本的成员函数setTarget首先是将参数node描述的一个Render Node保存在成员变量mTarget中,接着再通过调用RenderNode类的成员函数addAniamtor将当前正在处理的动画注册到Render Thread中去。       
 
       RenderNode类的成员函数addAniamtor的实现如下所示:
      这个函数定义在文件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实现,如下所示:
  这个函数定义在文件frameworks/base/core/jni/android_view_RenderNode.cpp中。
       参数renderNodePtr指向的是Native层的一个RenderNode对象,该RenderNode对象与要显示动画的View关联。参数animatorPtr指向的Native层的一个RenderPropertyAnimator对象,该RenderPropertyAnimator对象描述的就是要显示的动画。这里调用RenderNode类的成员函数addAnimator将参数animatorPtr描述的动画保存在参数renderNodePtr描述的Render Node的内部。
 
       RenderNode类的成员函数addAnimator的实现如下所示:
  这个函数定义在文件frameworks/base/libs/hwui/RenderNode.cpp中。
       RenderNode类的成员变量mAnimatorManager描述的是一个AnimatorManager对象。这个AnimatorManager对象用来管理一个RenderNode对象所关联的动画。因此,RenderNode类的成员函数addAnimator所做的事情就是将参数animator描述的动画保存在当前正在处理的RenderNode对象内部的一个AnimatorManager对象中,这是通过调用AnimatorManager类的成员函数addAnimator完成的。
 
       AnimatorManager类的成员函数addAnimator的实现如下所示:
  这个函数定义在文件frameworks/base/libs/hwui/AnimatorMaager.cpp中。
       在分析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的实现如下所示:
这个函数定义在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。
       当ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer的值不等于null的时候,它指向的是一个ThreadedRenderer对象。在这种情况下,ViewRootImpl类的成员函数registerAnimatingRenderNode会直接调用该ThreadedRenderer对象的成员函数registerAnimatingRenderNode将参数animator描述的一个Render Node注册到Render Thread中去,以便Render Thread知道哪些Render Node有新的动画需要显示。
 
       另一方面,当ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer的值等于null的时候,这意味着应用程序窗口使用软件渲染或者使用硬件加速渲染但是硬件加速渲染环境还没有初始化好。在这两种情况下,都是先将参数animator描述的Render Node保存在成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mPendingAnimatingRenderNodes描述的一个列表中等待处理。
 
       从前面Android应用程序UI硬件加速渲染的Display List构建过程分析一文可以知道,当使用硬件加速渲染时,应用程序窗口的渲染是从调用ThreadedRenderer类的成员函数draw开始的,它的实现如下所示:
 这个函数定义在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。 
       ThreadedRenderer类的成员函数draw在调用成员函数updateRootDisplayList构建应用程序窗口的Display List之后,并且调用成员函数nSyncAndDrawFrame渲染应用程序窗口的Display List之前,会调用成员函数registerAnimatingRenderNode处理保存在上面提到的AttachInfo对象的成员变量mPendingAnimatingRenderNodes描述的一个列表中的每一个Render Node。
 
       ThreadedRenderer类的成员函数registerAnimatingRenderNode的实现如下所示:
    这个函数定义在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。 
       ThreadedRenderer类的成员函数registerAnimatingRenderNode调用另外一个成员函数nRegisterAnimatingRenderNode将参数animator描述的一个Render Node注册到Render Thread中去。
 
       ThreadedRenderer类的成员函数nRegisterAnimatingRenderNode是一个JNI函数,由Native层的函数android_view_ThreadedRenderer_registerAnimatingRenderNode实现,如下所示:
 这个函数定义在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
       参数rootNodePtr指向的是一个RootRenderNode对象,该RootRenderNode对象描述的是应用程序窗口的Root Render Node,这里主要就是将参数animatorNodePtr描述的一个Render Node注册在上述的RootRenderNode对象的内部,这是通过调用RootRenderNode类的成员函数attachAnimatingNode实现的。
 
       RootRenderNode类的成员函数attachAnimatingNode的实现如下所示:
 这个函数定义在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
       RootRenderNode类的成员函数attachAnimatingNode将参数animatingNode描述的一个Render Node保存成员变量mPendingAnimatingRenderNodes描述的一个Vector中。保在这个Vector中的Render Node将会在应用程序窗口的下一帧被渲染时得到处理。
 
      从前面Android应用程序UI硬件加速渲染的Display List渲染过程分析一文可以知道,Render Thread在渲染应用程序窗口的下一帧时,会调用CanvasContext类的成员函数prepareTree将应用程序窗口的Display List从Main Thread同步到Render Thread,如下所示:
  这个函数定义定义在文件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对象,如下所示:
  这个函数定义在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
      在调用RenderProxy类的构造函数创建一个RenderProxy对象的时候,传递进去的第三个参数是一个ContextFactoryImpl对象。
 
      我们继续看RenderProxy类的构造函数的实现,如下所示:
   这个函数定义在文件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类的构造函数的实现如下所示:
   这个函数定义在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
       从这里就可以看出,CanvasContext的成员变量mAnimationContext指向的一个AnimationContext对象是由参数contextFactory描述的一个IContextFactory接口的成员函数createAnimationContext创建的。
 
       从前面的调用过程可以知道,参数contextFactory指向的实际上是一个ContextFactoryImpl对象,它的成员函数createAnimationContext的实现如下所示:
  这个函数定义在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
       ContextFactoryImpl类的成员函数createAnimationContext创建的是一个AnimationContextBridge对象,由此就可见,CanvasContext的成员变量mAnimationContext实际指向的一个AnimationContextBridge对象。
 
       明白了这一点之后,回到前面分析的CanvasContext类的成员函数prepareTree中,它在同步应用程序窗口的Display List之前,调用了AnimationContextBridge类的成员函数startFrame执行一些动画显示的准备工作。
 
       AnimationContextBridge类的成员函数startFrame的实现如下所示:
   这个函数定义在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
       参数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的实现如下所示:
   这个函数定义在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
       RootRenderNode类的成员函数doAttachAnimatingNodes遍历保存在成员变量mPendingAnimatingRenderNodes描述的一个Vector中的每一个RenderNode对象,并且分别调用参数context描述的一个AnimationContextBridge对象的成员函数addAnimatingRenderNode对它们进行处理。
 
       AnimationContextBridge类的成员函数addAnimatingRenderNode是从父类AnimationContext继承下来的,它的实现如下所示:
   这个函数定义在文件frameworks/base/libs/hwui/AnimationContext.cpp中。
       参数node指向的是一个RenderNode对象,调用它的成员函数animators可以获得它内部的一个AnimatorManager对象。如果该AnimatorManager对象还没有设置过一个AnimationHandle对象,那么AnimationContext类的成员函数addAnimatingRenderNode就为其设置一个AnimationHandle对象,并且这个AnimationHandle对象会通过AnimationContext类的成员函数addAnimationHandle保存在当前正在处理的一个AnimationContext对象的内部。
 
       AnimationContext类的成员函数addAnimationHandle的实现如下所示:
   这个函数定义在文件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的实现如下所示:
 这个函数定义在文件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的实现如下所示:
 这个函数定义在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。
       AnimationContextBridge类的成员函数runRemainingAnimations主要是通过调用父类AnimationContext的成员函数runRemainingAnimations更新剩下的还未完成的动画
 
       AnimationContext类的成员函数runRemainingAnimations的实现如下所示:
这个函数定义在文件frameworks/base/libs/hwui/AnimationContext.cpp中。
       前面提到,应用程序窗口当前帧要显示动画都记录在AnimationContext类的成员mCurrentFrameAnimations描述的一个AnimationHandle列表中。因此,AnimationContext类的成员函数runRemainingAnimations就遍历这个列表中的每一个AnimationHandle,并且获得与其关联的一个AnimatorManager。有了这些AnimatorManager之的,就可以调用它们的成员函数pushStaging和animateNoDamage来执行动画,其实就是计算动画的下一帧参数,以便应用在目标Render Node上。
 
       AnimatorManager类的成员函数pushStaging的实现如下所示:
 这个函数定义在文件frameworks/base/libs/hwui/AnimatorManager.cpp中。
       从前面的分析可以知道,一开始的时候,应用程序窗口新增加的动画都保存在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执行动画,它的实现如下所示:
 这个函数定义在文件frameworks/base/libs/hwui/AnimatorManager.cpp中。
       AniamtorManager类的成员函数animateNoDamage通过调用另外一个成员函数animateCommon来执行当前正在处理的AnimatorManager对象关联的动画,也就是某一个Render Node关联的动画
 
       AniamtorManager类的成员函数animateCommon的实现如下所示:
  这个函数定义在文件frameworks/base/libs/hwui/AnimatorManager.cpp中。
       前面提到,当前正在处理的AnimatorManager对象关联的动画都保存其成员变量mAnimators描述的一个Vector,因此这里就会通过函数std::remove_if来遍历这些动画,并且对于每一个动画,都通过本地变量functor描述的一个AnimatorFunctor对象的操作符重载函数()来执行它的当前帧。
 
       AnimatorFunctor类的操作符重载函数()执行完成动画的当前帧之后,如果动画已经完成,即没有下一帧了,那么它就会返回一个true值给调用者,这样会导致函数std::remove_if将该动画移动至列表的末尾位置。
 
       AnimatorFunctor类的操作符重载函数()的实现如下所示:
 这个函数定义在文件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的实现如下所示:
   这个函数定义在文件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,允许在一个动画执行之前,设置为是否可以异步执行,如下所示:
 这个函数定义在文件frameworks/base/core/java/android/view/RenderNodeAnimator.java中。
       当参数mayRunAsync的值等于true时,就表示将一个动画设置为异步动画;否则的话,就设置为同步动画。这个信息会通过调用RenderNodeAnimator类的另外一个成员函数nSetAllowRunningAsync同步到Native层关联的同样是用来描述动画的一个BaseRenderNodeAnimator对象中去。
 
       RenderNodeAnimator类的成员函数nSetAllowRunningAsync是一个JNI函数,由Native层的函数setAllowRunningAsync实现,如下所示:
   这个函数定义在文件frameworks/base/core/jni/android_view_RenderNodeAnimator.cpp中。
       参数animatorPtr描述的就是Native用来描述动画的一个BaseRenderNodeAnimator对象,这里调用它的成员函数setAllowRunningAsync设置它是一个同步动画还是一个异步动画
 
        BaseRenderNodeAnimator类的setAllowRunningAsync的实现如下所示:
 这两个函数定义在文件frameworks/base/libs/hwui/Animator.h中。
       BaseRenderNodeAnimator类的setAllowRunningAsync将动画的同步异步信息记录在成员变量mMayRunAsync中。这样就可以通过调用另外一个成员函数mayRunAsync就可以知道一个动画是同步的还是异步,如前面分析的AnimatorFunctor类的操作符重载函数()所示。
 
       了解了一个动画的同步异步概念之后,回到前面分析的CanvasContext类的成员函数prepareTree中,当它同步完成应用程序窗口的Display List以及处理过应用程序窗口的动画之后,如果应用程序窗口还有未完成的动画,并且这些都是异步动画,那么就会注册一个IFrameCallback接口到Render Thread中。为了方便描述,我们重新将这部分代码列出来,如下所示:
  这个函数定义在文件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的实现如下所示:
 这个函数定义在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。
       CanvasContext类的成员函数doFrame首先是构造一个mode值等于TreeInfo::MODE_RT_ONLY的TreeInfo对象,接着再使用这个TreeInfo对象来调用我们前面分析过的成员函数prepareTree。从前面Android应用程序UI硬件加速渲染的Display List渲染过程分析一文可以知道,当传递给CanvasContext类的成员函数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硬件加速渲染技术简要介绍和学习计划一文。
暂无评论
  • 1:请一针见血的评论。
  • 2:评论需要审核通过后才能显示。
  • 3:评论字数限制在1000字以内。
  • 当前字数:0
热门文章
推荐文章
随机文章
关于本站 - 广告服务 - 版权声明 - 联系我们 - 友情链接 - 网站地图 - 帮助中心