分享

Android 4.4 Graphic系统详解(4)HWUI概述

 昵称3554661 2016-11-29

1 概述

Android从3.0(API Level 11)开始,在绘制View的时候支持硬件加速,充分利用GPU的特性,使得绘制更加平滑。
实质上就是Android3.0以前,几乎所有的图形绘制都是由Skia完成,Skia是一个向量绘图库,使用CPU来进行运算;所以从Android3.0 开始,Google用hwui取代了Skia,准确的说,是推荐取代,因为Opengl的支持不完全,有少量图形api仍由Skia完成,多数view的绘制通过HWUI模块使用openGL的函数来实现。

由于硬件加速自身并非完美无缺,所以Android提供选项来打开或者关闭硬件加速,默认是关闭。一般我们会在两个级别上打开或者关闭硬件加速:

      Application级别:<applicationandroid:hardwareAccelerated="true" ...>
      Activity级别:<activity android:hardwareAccelerated="false" ...>

下面我们从代码级别来理解系统是如何实现硬件加速的。

2 开启硬件加速

在开始分析之前,我们先来看下ViewRootImpl中的一个基本流程图:
此处输入图片的描述
如上图所示,其实过程2就是打开硬件加速的过程,在ViewRootImpl的SetView函数中,通过调用enableHardwareAcceleration函数来开启硬件加速,我们来看下enableHardwareAcceleration的代码:

  1. private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {  
  2.         mAttachInfo.mHardwareAccelerated = false;  
  3.         mAttachInfo.mHardwareAccelerationRequested = false;  
  4.         final boolean hardwareAccelerated =  
  5.                 (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;  
  6.         if (hardwareAccelerated) {  
  7.             if (!HardwareRenderer.isAvailable()) {  
  8.                 return;  
  9.             }  
  10.             // Persistent processes (including the system) should not do  
  11.             // accelerated rendering on low-end devices.  In that case,  
  12.             // sRendererDisabled will be set.  In addition, the system process  
  13.             // itself should never do accelerated rendering.  In that case, both  
  14.             // sRendererDisabled and sSystemRendererDisabled are set.  When  
  15.             // sSystemRendererDisabled is set, PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED  
  16.             // can be used by code on the system process to escape that and enable  
  17.             // HW accelerated drawing.  (This is basically for the lock screen.)  
  18.   
  19.             final boolean fakeHwAccelerated = (attrs.privateFlags &  
  20.                     WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;  
  21.             final boolean forceHwAccelerated = (attrs.privateFlags &  
  22.                     WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;  
  23.   
  24.             if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled  
  25.                     && forceHwAccelerated)) {  
  26.                 // Don't enable hardware acceleration when we're not on the main thread  
  27.                 if (!HardwareRenderer.sSystemRendererDisabled &&  
  28.                         Looper.getMainLooper() != Looper.myLooper()) {  
  29.                     Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware "  
  30.                             + "acceleration outside of the main thread, aborting");  
  31.                     return;  
  32.                 }  
  33.   
  34.                 if (mAttachInfo.mHardwareRenderer != null) {  
  35.                     mAttachInfo.mHardwareRenderer.destroy(true);  
  36.                 }  
  37.   
  38.                 final boolean translucent = attrs.format != PixelFormat.OPAQUE;  
  39.                 mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);  
  40.                 if (mAttachInfo.mHardwareRenderer != null) {  
  41.                     mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());  
  42.                     mAttachInfo.mHardwareAccelerated =  
  43.                             mAttachInfo.mHardwareAccelerationRequested = true;  
  44.                 }  
  45.             } else if (fakeHwAccelerated) {  
  46.                 // The window had wanted to use hardware acceleration, but this  
  47.                 // is not allowed in its process.  By setting this flag, it can  
  48.                 // still render as if it was accelerated.  This is basically for  
  49.                 // the preview windows the window manager shows for launching  
  50.                 // applications, so they will look more like the app being launched.  
  51.                 mAttachInfo.mHardwareAccelerationRequested = true;  
  52.             }  
  53.         }  
  54.     }  

上面代码不长,而且注释很完善,所以我们主要来看下函数的最主体部分即可。

  1. static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {  
  2.        switch (glVersion) {  
  3.            case 2:  
  4.                return Gl20Renderer.create(translucent);  
  5.        }  
  6.        throw new IllegalArgumentException("Unknown GL version: " + glVersion);  
  7.    }  
  8.    static HardwareRenderer create(boolean translucent) {  
  9.            if (GLES20Canvas.isAvailable()) {  
  10.                return new Gl20Renderer(translucent);  
  11.            }  
  12.            return null;  
  13.    }  
  14.    GlRenderer(int glVersion, boolean translucent) {  
  15.            mGlVersion = glVersion;  
  16.            mTranslucent = translucent;  
  17.            loadSystemProperties(null);  
  18.    } 

我们发现这段代码最主要的作用就是创建了一个Gl20Renderer(继承自GlRenderer和HardwareRenderer)。创建时调用了通过loadSystemProperties函数加载了一些属性和调试选项,这里不展开,有兴趣可以详细研究一下。

创建好的Gl20Renderer保存在了mAttachInfo.mHardwareRenderer中,后面我们在使用硬件加速时就会用到。

3 硬件绘制

从前面图中我们可以知道,在ViewRootImpl绘制时,调用了draw函数。有两种选择,一种是软件绘制,一种是硬件绘制。

  1. if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {  
  2.               // Draw with hardware renderer.  
  3.               mIsAnimating = false;  
  4.               mHardwareYOffset = yoff;  
  5.               mResizeAlpha = resizeAlpha;  
  6.   
  7.               mCurrentDirty.set(dirty);  
  8.               dirty.setEmpty();  
  9.   
  10.               attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,  
  11.                       animating ? null : mCurrentDirty);  
  12.           } else {  
  13.               // If we get here with a disabled & requested hardware renderer, something went  
  14.               // wrong (an invalidate posted right before we destroyed the hardware surface  
  15.               // for instance) so we should just bail out. Locking the surface with software  
  16.               // rendering at this point would lock it forever and prevent hardware renderer  
  17.               // from doing its job when it comes back.  
  18.               // Before we request a new frame we must however attempt to reinitiliaze the  
  19.               // hardware renderer if it's in requested state. This would happen after an  
  20.               // eglTerminate() for instance.  
  21.               if (attachInfo.mHardwareRenderer != null &&  
  22.                       !attachInfo.mHardwareRenderer.isEnabled() &&  
  23.                       attachInfo.mHardwareRenderer.isRequested()) {  
  24.   
  25.                   try {  
  26.                       attachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight,  
  27.                               mHolder.getSurface());  
  28.                   } catch (OutOfResourcesException e) {  
  29.                       handleOutOfResourcesException(e);  
  30.                       return;  
  31.                   }  
  32.   
  33.                   mFullRedrawNeeded = true;  
  34.                   scheduleTraversals();  
  35.                   return;  
  36.               }  
  37.   
  38.               if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {  
  39.                   return;  
  40.               }  
  41.           }  
  42.       }  

软件绘制不在我们分析范围内,这里我们将在下一小节中来研究一下attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,animating ? null : mCurrentDirty)这个调用。


我们前面已经知道mHardwareRenderer其实是一个Gl20Renderer,那么我们来看下它的draw函数。

  1. void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,  
  2.         Rect dirty) {  
  3.     if (canDraw()) {  
  4.         if (surfaceState != SURFACE_STATE_ERROR) {  
  5.             HardwareCanvas canvas = mCanvas;  
  6.             attachInfo.mHardwareCanvas = canvas;  
  7.             dirty = beginFrame(canvas, dirty, surfaceState);  
  8.             DisplayList displayList = buildDisplayList(view, canvas);  
  9.             int saveCount = 0;  
  10.             int status = DisplayList.STATUS_DONE;  
  11.             try {  
  12.                 status = prepareFrame(dirty);  
  13.                 if(mSurface.isValid() && dirty != null) {  
  14.                         mSurface.setDirtyRect(dirty);  
  15.                 }  
  16.                 saveCount = canvas.save();  
  17.                 callbacks.onHardwarePreDraw(canvas);  
  18.   
  19.                 if (displayList != null) {  
  20.                     status |= drawDisplayList(attachInfo, canvas, displayList, status);  
  21.                 }   
  22.             } catch (Exception e) {  
  23.                 Log.e(LOG_TAG, "An error has occurred while drawing:", e);  
  24.             } finally {  
  25.                 callbacks.onHardwarePostDraw(canvas);  
  26.                 canvas.restoreToCount(saveCount);  
  27.                 view.mRecreateDisplayList = false;  
  28.   
  29.                 if (mDrawDelta > 0) {  
  30.                     mFrameCount++;  
  31.                     debugOverdraw(attachInfo, dirty, canvas, displayList);  
  32.                     debugDirtyRegions(dirty, canvas);  
  33.                     drawProfileData(attachInfo);  
  34.                 }  
  35.             }  
  36.             onPostDraw();  
  37.             swapBuffers(status);  
  38.         }  
  39.     }  
  40. }  

上面这几十行代码,就是硬件绘制的最重要的几个流程调用,我们看到draw函数主要分为如下几步,beginFrame,buildDisplayList,prepareFrame,drawDisplayList,onHardwarePostDraw,onPostDraw,swapBuffers。整个详细的调用过程如下图:
此处输入图片的描述
从上图中我们可以看出,整个内部详细的调用过程还是比较复杂的,我们这里仔细的一一讲解一下。

1 beginFrame

这个函数的作用很简单:Notifies EGL that the frame is about to be rendered。
功能主要通过android_view_HardwareRenderer_beginFrame这个JNI函数实现:

  1. static void android_view_HardwareRenderer_beginFrame(JNIEnv* env, jobject clazz,  
  2.         jintArray size) {  
  3.     EGLDisplay display = eglGetCurrentDisplay();  
  4.     EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);  
  5.     if (size) {  
  6.         EGLint value;  
  7.         jint* storage = env->GetIntArrayElements(size, NULL);  
  8.         eglQuerySurface(display, surface, EGL_WIDTH, &value);  
  9.         storage[0] = value;  
  10.         eglQuerySurface(display, surface, EGL_HEIGHT, &value);  
  11.         storage[1] = value;  
  12.         env->ReleaseIntArrayElements(size, storage, 0);  
  13.     }  
  14.     eglBeginFrame(display, surface);  

在BeginFrame() 里获取EGLDisplay(用于显示) 和一个EGLSurface,OpenGL将在这个Surface上进行绘图。
eglBeginFrame主要是校验参数的合法性。

2 buildDisplayList

buildDisplayList是一个很重要也比较复杂的函数,通过这个函数,我们真正完成了视图绘制的录制工作,并将操作指令保存到了native层的DisplayList中,我们这里重点讲一下。

  1. private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {  
  2.     if (mDrawDelta <= 0) {//如果根本没有新的绘制发生,使用之前的displaylist即可  
  3.         return view.mDisplayList;  
  4.     }  
  5.     view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)  
  6.             == View.PFLAG_INVALIDATED;  
  7.     view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;  
  8.     long buildDisplayListStartTime = startBuildDisplayListProfiling();  
  9.     canvas.clearLayerUpdates();  
  10.     Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");  
  11.     DisplayList displayList = view.getDisplayList();  
  12.     Trace.traceEnd(Trace.TRACE_TAG_VIEW);  
  13.     endBuildDisplayListProfiling(buildDisplayListStartTime);  
  14.     return displayList;  
  15. }  

其实主要逻辑是通过view类的getDisplayList()方法实现的,这个方法通过递归的方式获得了整个View树的DisplayList:

  1. private DisplayList getDisplayList(DisplayList displayList, boolean isLayer) {  
  2.         if (((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 ||  
  3.                 displayList == null || !displayList.isValid() ||  
  4.                 (!isLayer && mRecreateDisplayList))) {  
  5.             // Don't need to recreate the display list, just need to tell our  
  6.             // children to restore/recreate theirs  
  7.             if (displayList != null && displayList.isValid() &&  
  8.                     !isLayer && !mRecreateDisplayList) {  
  9.                 mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;  
  10.                 mPrivateFlags &= ~PFLAG_DIRTY_MASK;  
  11.                 dispatchGetDisplayList();  
  12.                 return displayList;  
  13.             }  
  14.             if (!isLayer) {  
  15.                 // If we got here, we're recreating it. Mark it as such to ensure that  
  16.                 // we copy in child display lists into ours in drawChild()  
  17.                 mRecreateDisplayList = true;  
  18.             }  
  19.             if (displayList == null) {  
  20.                 displayList = mAttachInfo.mHardwareRenderer.createDisplayList(getClass().getName());  
  21.                 // If we're creating a new display list, make sure our parent gets invalidated  
  22.                 // since they will need to recreate their display list to account for this  
  23.                 // new child display list.  
  24.                 invalidateParentCaches();  
  25.             }  
  26.             boolean caching = false;  
  27.             int width = mRight - mLeft;  
  28.             int height = mBottom - mTop;  
  29.             int layerType = getLayerType();  
  30.             final HardwareCanvas canvas = displayList.start(width, height);  
  31. ...  

上面函数相当长,我们首先分析前半部分。暂时只看displaylist新创建时会走的逻辑:函数前半部分的流程基本是createDisplayList,invalidateParentCaches,displayList.start,我们来一点点分析。

2.1 createDisplayList

首先我们要创建一个displaylist,这里实际上只是创建了一个GLES20DisplayList类。

  1. public DisplayList createDisplayList(String name) {  
  2.     return new GLES20DisplayList(name);  
  3. }  

GLES20DisplayList是DisplayList的子类,这是一个影子类,没有实际用途,主要用来管理Native层的DisplayList的销毁。Native层的DisplayList在后面的start函数中才被真正创建。

2.2 invalidateParentCaches

这个函数的作用主要是,一旦我们重建了DisplayList,那么这个view必须清空之前的缓存,我们通过这个函数调用来清空缓存。

  1. protected void invalidateParentCaches() {  
  2.     if (mParent instanceof View) {  
  3.         ((View) mParent).mPrivateFlags |= PFLAG_INVALIDATED;  
  4.     }  
  5. }  

2.3 displayList.start

下面看下displayList.start函数:

  1. public HardwareCanvas start(int width, int height) {  
  2.        mValid = false;  
  3.        mCanvas = GLES20RecordingCanvas.obtain(this);  
  4.        mCanvas.start();  
  5.        mCanvas.setViewport(width, height);  
  6.        // The dirty rect should always be null for a display list  
  7.        mCanvas.onPreDraw(null);  
  8.        return mCanvas;  
  9.    }  

2.3.1 GLES20RecordingCanvas.obtain

start函数真正开始构建DisplayList的上下文,先来看下obtain函数:

  1. static GLES20RecordingCanvas obtain(GLES20DisplayList displayList) {  
  2.        GLES20RecordingCanvas canvas = sPool.acquire();  
  3.        if (canvas == null) {  
  4.            canvas = new GLES20RecordingCanvas();  
  5.        }  
  6.        canvas.mDisplayList = displayList;  
  7.        return canvas;  
  8.    }  

obtain函数的作用从名字中就可以看出来,主要作用是获取到一个GLES20RecordingCanvas,这个类是 HardwareCanvas和GLES20Canvas的子类。

HardwareCanvas是一个抽象类,如果系统属性和应用程序指定使用硬件加速(现已成为默认),它将会被View(通过AttachInfo,如 下图所示) 引用来完成所有的图形绘制工作。GLES20Canvas 则是hardwareCanvas的实现,但它也只是一层封装而已,真正的实现在Native 层,通过jni (andriod_view_gles20Canvas.cpp)接口来访问底层的Renderer, 进而执行OpenGL的命令。 此外,GLES20Canvas还提供了一些静态接口,用于创建各类Renderer对象。

GLES20RecordingCanvas 继承GLES20Canvas, 通过它调用的OpenGL命令将会存储在DisplayList里面,而不会立即执行。

  1. protected GLES20Canvas(boolean record, boolean translucent) {  
  2.         mOpaque = !translucent;  
  3.         if (record) {  
  4.             mRenderer = nCreateDisplayListRenderer();  
  5.         } else {  
  6.             mRenderer = nCreateRenderer();  
  7.         }  
  8.         setupFinalizer();  
  9.     } 

我们看到GLES20Canvas在创建时,由于我们现在需要录制命令,所以我们这里调用了nCreateDisplayListRenderer函数,创建的真正的用于渲染的本地对象DisplayListRenderer:

  1. static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env,  
  2.         jobject clazz) {  
  3.     return new DisplayListRenderer;  
  4. }  

DisplayListRenderer是OpenGLRenderer的子类,相比于OpenGLRenderer是直接绘制,DisplayListRenderer则是将绘制命令保存在它内部的mDisplayListData中,mDisplayListData的类型是DisplayListData:

  1. class DisplayListData : public LightRefBase<DisplayListData> {  
  2. public:  
  3.     LinearAllocator allocator;  
  4.     Vector<DisplayListOp*> displayListOps;  
  5. };  

我们看到这里的重点必然是这个容器displayListOps,一定是它存储了绘制命令DisplayListOp,至于DisplayListOp的详细内容,我们后面再展开。

这样,通过nCreateDisplayListRenderer函数,我们创建了一个本地的DisplayListRenderer对象,他会将绘制命令保存在一个DisplayListData类的DisplayListOp容器里。
现在我们再次回到GLES20Canvas的创建过程中来,调用完nCreateDisplayListRenderer后,程序又继续调用了setupFinalizer,这个我们从名字就可以猜出来这是用于这个canvas回收的,这里我们就不再展开。

2.3.2 GLES20RecordingCanvas.start

回到displayList.start函数中来,创建好GLES20RecordingCanvas之后,继续调用了它的start函数。这个函数主要是清空自己和它的孩子的所有display的引用,这里不再展开。

2.3.3 GLES20RecordingCanvas.setViewport

继续调用setViewport来设置视口:

  1.   public void setViewport(int width, int height) {  
  2.         mWidth = width;  
  3.         mHeight = height;  
  4.         nSetViewport(mRenderer, width, height);  
  5.     }  
  6.     static void android_view_GLES20Canvas_setViewport(JNIEnv* env, jobject clazz,  
  7.         OpenGLRenderer* renderer, jint width, jint height) {  
  8.     renderer->setViewport(width, height);  
  9. }  
  10. void DisplayListRenderer::setViewport(int width, int height) {  
  11.     mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);  
  12.     mWidth = width;  
  13.     mHeight = height;  
  14. }  
  15.   
  16. void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) {  
  17.     loadIdentity();  
  18.     data[kScaleX] = 2.0f / (right - left);  
  19.     data[kScaleY] = 2.0f / (top - bottom);  
  20.     data[kScaleZ] = -2.0f / (far - near);  
  21.     data[kTranslateX] = -(right + left) / (right - left);  
  22.     data[kTranslateY] = -(top + bottom) / (top - bottom);  
  23.     data[kTranslateZ] = -(far + near) / (far - near);  
  24.     mType = kTypeTranslate | kTypeScale | kTypeRectToRect;  
  25. }  

我们看到上层调用了视口,下层实际上还是通过DisplayListRenderer做了具体实现,主要是做了一个正交投影,具体计算我们这里暂时先不研究。
其实大多数opengl在Canvas层面上的调用都是这样,底层通过render实现了类似OpenGl的实现,后面类似函数我们将不再展开。

2.3.4 GLES20RecordingCanvas.onPreDraw

GLES20RecordingCanvas.onPreDraw函数其实也很简单,先来看代码:

  1. public int onPreDraw(Rect dirty) {  
  2.      if (dirty != null) {  
  3.          return nPrepareDirty(mRenderer, dirty.left, dirty.top, dirty.right, dirty.bottom,  
  4.                  mOpaque);  
  5.      } else {  
  6.          return nPrepare(mRenderer, mOpaque);  
  7.      }  
  8.  }  
  9.  static int android_view_GLES20Canvas_prepare(JNIEnv* env, jobject clazz,  
  10.      OpenGLRenderer* renderer, jboolean opaque) {  
  11.  return renderer->prepare(opaque);  
  12.  }  
  13.   
  14.  status_t OpenGLRenderer::prepare(bool opaque) {  
  15.      return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);  
  16.  }  
  17.  status_t OpenGLRenderer::prepareDirty(float left, float top,  
  18.      float right, float bottom, bool opaque) {  
  19.   setupFrameState(left, top, right, bottom, opaque);  
  20.  // Layer renderers will start the frame immediately  
  21.  // The framebuffer renderer will first defer the display list  
  22.  // for each layer and wait until the first drawing command  
  23.  // to start the frame  
  24.   if (mSnapshot->fbo == 0) {  
  25.      syncState();  
  26.      updateLayers();  
  27.   } else {  
  28.          return startFrame();  
  29.      }  
  30.      return DrawGlInfo::kStatusDone;  
  31.  }  

本身这个onPreDraw其实是很简单的,无非是通过canvas来设置要显示的脏区域。最终逻辑依然是通过native层的OpenGLRenderer来实现,当然,这个实现最后变得有点复杂,因为这里又引入了一个新的概念,SnapShot:

  1. void OpenGLRenderer::setupFrameState(float left, float top,  
  2.         float right, float bottom, bool opaque) {  
  3.     mCaches.clearGarbage();  
  4.     mOpaque = opaque;  
  5.     mSnapshot = new Snapshot(mFirstSnapshot,  
  6.             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);  
  7.     mSnapshot->fbo = getTargetFbo();  
  8.     mSaveCount = 1;  
  9.     mSnapshot->setClip(left, top, right, bottom);  
  10.     mTilingClip.set(left, top, right, bottom);  
  11. }  

我们这章引入的新概念实在过多,因此我们这里先跳过这个概念,可以参考这篇博客来先了解一下这个快照的概念,我们后面将单独开章节讲这个问题。

2.4 getDisplayList中段小结

由于getDisplayList实在是逻辑比较长,涉及的新概念比较多,我们这里暂时先暂停一下,回顾下前半部分的逻辑::函数前半部分的流程基本是createDisplayList,invalidateParentCaches,displayList.start。createDisplayList的作用是创建了一个Java层的GLES20DisplayList,而displayList.start重点则是创建了一个native层GLES20RecordingCanvas,在创建Canvas的同时创建了DisplayListRenderer,DisplayListRenderer将需要执行的命令保存在内部的displaylist列表中,然后设置了视口,设置了脏区域,这里我们跳过了Snapshot这个新概念。
下面我们继续来看下getDisplayList后半段的代码。

  1. {  
  2. ...  
  3.  try {  
  4.                 if (!isLayer && layerType != LAYER_TYPE_NONE) {  
  5.                     if (layerType == LAYER_TYPE_HARDWARE) {  
  6.                         final HardwareLayer layer = getHardwareLayer();  
  7.                         if (layer != null && layer.isValid()) {  
  8.                             canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);  
  9.                         } else {  
  10.                             if (layer != null && !layer.isValid()) Log.e("ViewSystem""View #2 getHardwareLayer() is not valid.");  
  11.                             canvas.saveLayer(0, 0, mRight - mLeft, mBottom - mTop, mLayerPaint,  
  12.                                     Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |  
  13.                                             Canvas.CLIP_TO_LAYER_SAVE_FLAG);  
  14.                         }  
  15.                         caching = true;  
  16.                     } else {  
  17.                         buildDrawingCache(true);  
  18.                         Bitmap cache = getDrawingCache(true);  
  19.                         if (cache != null) {  
  20.                             canvas.drawBitmap(cache, 0, 0, mLayerPaint);  
  21.                             caching = true;  
  22.                         }  
  23.                     }  
  24.                 } else {  
  25.                     computeScroll();  
  26.                     canvas.translate(-mScrollX, -mScrollY);  
  27.                     if (!isLayer) {  
  28.                         mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;  
  29.                         mPrivateFlags &= ~PFLAG_DIRTY_MASK;  
  30.                     }  
  31.                     // Fast path for layouts with no backgrounds  
  32.                     if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {  
  33.                         dispatchDraw(canvas);  
  34.                         if (mOverlay != null && !mOverlay.isEmpty()) {  
  35.                             mOverlay.getOverlayView().draw(canvas);  
  36.                         }  
  37.                     } else {  
  38.                         draw(canvas);  
  39.                     }  
  40.                 }  
  41.             }   
  42.     ...  

2.5 开始生成绘制命令

上面这段代码涉及到两条分支,第一条分支又涉及到了新概念,layerType和HardwareLayer,这条分支比较复杂,我们单独放在下一章中单独讲解,我们现在重点看下第二条分支的代码:

  1. {  
  2. ...  
  3. else {  
  4.                     computeScroll();  
  5.                     canvas.translate(-mScrollX, -mScrollY);  
  6.                     if (!isLayer) {  
  7.                         mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;  
  8.                         mPrivateFlags &= ~PFLAG_DIRTY_MASK;  
  9.                     }  
  10.                     // Fast path for layouts with no backgrounds  
  11.                     if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {  
  12.                         dispatchDraw(canvas);  
  13.                         if (mOverlay != null && !mOverlay.isEmpty()) {  
  14.                             mOverlay.getOverlayView().draw(canvas);  
  15.                         }  
  16.                     } else {  
  17.                         draw(canvas);  
  18.                     }  
  19.                 }  
  20. }  

computeScroll计算滚动位置,和Graphic关系不大,不展开。

translate函数我们在上一节 Android 4.4 Graphic系统详解(4) 一个view的绘制之旅 中已经讲过了,这里不再展开。
其实就是创建了一个Op类的对象,然后将它保存在了DisplayListData的displayListOps容器里面,便于后面的执行。

回到view类getdisplaylist函数中来,说完了translate,下面就是重要的绘制生成流程了。

  1. {                 
  2.    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {  
  3.             dispatchDraw(canvas);  
  4.             if (mOverlay != null && !mOverlay.isEmpty()) {  
  5.                 mOverlay.getOverlayView().draw(canvas);  
  6.             }  
  7.         } else {  
  8.          draw(canvas);  
  9.     }  
  10. }  

其中dispatchDraw是通知view的所有孩子进行绘制(?),其实流程应该都是类似的,因此我们直接看当前view的draw流程,此函数将把View自身及其所有的子子孙孙绘制到给定的画布上。其画图过程主要分为以下六步:

  1) 画背景
  2) 如果需要,保存画布的层为未来的淡入淡出做好准备
  3) 画View本身的内容
  4) 画它的孩子
  5) 如果需要,画淡入淡出的边缘并恢复层
  6) 画装饰部分(如:滚动条)

上述六步中,2,5步常规情况下不需要,因此我们暂时跳过,只重点分析1,3,6步。

  1. public void draw(Canvas canvas) {  
  2.       // Step 1, draw the background, if needed  
  3.       int saveCount;  
  4.   
  5.       if (!dirtyOpaque) {  
  6.           final Drawable background = mBackground;  
  7.           if (background != null) {  
  8.               final int scrollX = mScrollX;  
  9.               final int scrollY = mScrollY;  
  10.               if (mBackgroundSizeChanged) {  
  11.                   background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);  
  12.                   mBackgroundSizeChanged = false;  
  13.               }  
  14.               if ((scrollX | scrollY) == 0) {  
  15.                   background.draw(canvas);  
  16.               } else {  
  17.                   canvas.translate(scrollX, scrollY);  
  18.                   background.draw(canvas);  
  19.                   canvas.translate(-scrollX, -scrollY);  
  20.               }  
  21.           }  
  22.       }  
  23.   
  24.       // skip step 2 & 5 if possible (common case)  
  25.       final int viewFlags = mViewFlags;  
  26.       boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;  
  27.       boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;  
  28.       if (!verticalEdges && !horizontalEdges) {  
  29.           // Step 3, draw the content  
  30.           if (!dirtyOpaque) onDraw(canvas);  
  31.           // Step 4, draw the children  
  32.           dispatchDraw(canvas);  
  33.           // Step 6, draw decorations (scrollbars)  
  34.           onDrawScrollBars(canvas);  
  35.           if (mOverlay != null && !mOverlay.isEmpty()) {  
  36.               mOverlay.getOverlayView().dispatchDraw(canvas);  
  37.           }  
  38.           // we're done...  
  39.           return;  
  40.       }  
  41.   }  

2.5.1 绘制背景

绘制背景很简单,只是调用了 background.draw(canvas)函数,其中background是一个Drawable类型,关于Drawable的绘制,依然可以参考“一个view的绘制之旅”一章中的Drawable的绘制小节讲解,这里不再展开。

2.5.2 onDraw 绘制内容

这一过程比较复杂,其实也就是我们在上一章“一个view的绘制之旅”中讲到的全部内容,具体内容可参考上一章。

2.5.3 绘制滚动条

绘制滚动条的代码比较长,但是按照我们一贯的习惯,我们只关注和Graphic相关的部分,在onDrawScrollBars函数中有横向和纵向ScrollBar的区别,但是对于下层的Graphic并无这种区别,都是调用ScrollBarDrawable的draw函数:

  1. public void draw(Canvas canvas) {  
  2.     boolean drawTrack = true;  
  3.     Rect r = getBounds();  
  4.     if (drawTrack) {  
  5.         drawTrack(canvas, r, vertical);  
  6.     }  
  7.     if (drawThumb) {  
  8.         int size = vertical ? r.height() : r.width();  
  9.         int thickness = vertical ? r.width() : r.height();  
  10.         int length = Math.round((float) size * extent / range);  
  11.         int offset = Math.round((float) (size - length) * mOffset / (range - extent));  
  12.         drawThumb(canvas, r, offset, length, vertical);  
  13.     }  
  14. }  

drawThumb中逻辑转来转去,最终其实还是在drawThumb中再次调用了Drawable的draw函数,这里不再详述。

最终,通过漫长的过程,我们终于完成了displaylist的构建,在完成了buildDisplayList的调用之后,我们回到Gl20Renderer类,继续来看下的draw函数,接下来就该Displaylist的真正执行了。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多