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中的一个基本流程图:

android开启hls服务 android hwui_android开启hls服务


如上图所示,其实过程2就是打开硬件加速的过程,在ViewRootImpl的SetView函数中,通过调用enableHardwareAcceleration函数来开启硬件加速,我们来看下enableHardwareAcceleration的代码:

[cpp] view plain copy

1. private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {  
2. false;  
3. 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. "Attempting to initialize hardware "  
30. "acceleration outside of the main thread, aborting");  
31. return;  
32.                 }  
33.   
34. if (mAttachInfo.mHardwareRenderer != null) {  
35. 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. 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. true;  
52.             }  
53.         }  
54.     }


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


[cpp] view plain copy

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. int glVersion, boolean translucent) {  
15.            mGlVersion = glVersion;  
16.            mTranslucent = translucent;  
17.            loadSystemProperties(null);  
18.    }

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

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

3 硬件绘制

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


[cpp] view plain copy

1. if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {  
2. // Draw with hardware renderer.  
3. false;  
4.               mHardwareYOffset = yoff;  
5.               mResizeAlpha = resizeAlpha;  
6.   
7.               mCurrentDirty.set(dirty);  
8.               dirty.setEmpty();  
9.   
10. 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. 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函数。


[cpp] view plain copy

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. "An error has occurred while drawing:", e);  
24.             } finally {  
25.                 callbacks.onHardwarePostDraw(canvas);  
26.                 canvas.restoreToCount(saveCount);  
27. 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。整个详细的调用过程如下图:

android开启hls服务 android hwui_android_02


从上图中我们可以看出,整个内部详细的调用过程还是比较复杂的,我们这里仔细的一一讲解一下。

1 beginFrame

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


[cpp] view plain copy

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中,我们这里重点讲一下。


[cpp] view plain copy

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. "getDisplayList");  
11.     DisplayList displayList = view.getDisplayList();  
12.     Trace.traceEnd(Trace.TRACE_TAG_VIEW);  
13.     endBuildDisplayListProfiling(buildDisplayListStartTime);  
14. return displayList;  
15. }

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


[cpp] view plain copy

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. 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. 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类。


[cpp] view plain copy

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

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

2.2 invalidateParentCaches

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


[cpp] view plain copy

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


2.3 displayList.start

下面看下displayList.start函数:


[cpp] view plain copy

1. public HardwareCanvas start(int width, int height) {  
2. false;  
3. 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函数:


[cpp] view plain copy

1. static GLES20RecordingCanvas obtain(GLES20DisplayList displayList) {  
2.        GLES20RecordingCanvas canvas = sPool.acquire();  
3. if (canvas == null) {  
4. 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里面,而不会立即执行。


[cpp] view plain copy

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:


[cpp] view plain copy

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

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


[cpp] view plain copy

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来设置视口:


[cpp] view plain copy

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函数其实也很简单,先来看代码:


[cpp] view plain copy

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. bool opaque) {  
15. return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);  
16.  }  
17. 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:


[cpp] view plain copy

1. void OpenGLRenderer::setupFrameState(float left, float top,  
2. float right, float bottom, bool opaque) {  
3.     mCaches.clearGarbage();  
4.     mOpaque = opaque;  
5. 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后半段的代码。


[cpp] view plain copy

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. true;  
16. else {  
17. true);  
18. true);  
19. if (cache != null) {  
20.                             canvas.drawBitmap(cache, 0, 0, mLayerPaint);  
21. 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,这条分支比较复杂,我们单独放在下一章中单独讲解,我们现在重点看下第二条分支的代码:


[cpp] view plain copy

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,下面就是重要的绘制生成流程了。


[cpp] view plain copy

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步。


[cpp] view plain copy

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. int scrollX = mScrollX;  
9. int scrollY = mScrollY;  
10. if (mBackgroundSizeChanged) {  
11.                   background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);  
12. 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. 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函数:


[cpp] view plain copy

1. public void draw(Canvas canvas) {  
2. 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的真正执行了。