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的代码:

                               [cpp] view plain copy                     
  1. privatevoidenableHardwareAcceleration(WindowManager.LayoutParamsattrs){
  2. mAttachInfo.mHardwareAccelerated=false;
  3. mAttachInfo.mHardwareAccelerationRequested=false;
  4. finalbooleanhardwareAccelerated=
  5. (attrs.flags&WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)!=0;
  6. if(hardwareAccelerated){
  7. if(!HardwareRenderer.isAvailable()){
  8. return;
  9. }
  10. //Persistentprocesses(includingthesystem)shouldnotdo
  11. //acceleratedrenderingonlow-enddevices.Inthatcase,
  12. //sRendererDisabledwillbeset.Inaddition,thesystemprocess
  13. //itselfshouldneverdoacceleratedrendering.Inthatcase,both
  14. //sRendererDisabledandsSystemRendererDisabledareset.When
  15. //sSystemRendererDisabledisset,PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
  16. //canbeusedbycodeonthesystemprocesstoescapethatandenable
  17. //HWaccelerateddrawing.(Thisisbasicallyforthelockscreen.)
  18. finalbooleanfakeHwAccelerated=(attrs.privateFlags&
  19. WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED)!=0;
  20. finalbooleanforceHwAccelerated=(attrs.privateFlags&
  21. WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED)!=0;
  22. if(!HardwareRenderer.sRendererDisabled||(HardwareRenderer.sSystemRendererDisabled
  23. &&forceHwAccelerated)){
  24. //Don'tenablehardwareaccelerationwhenwe'renotonthemainthread
  25. if(!HardwareRenderer.sSystemRendererDisabled&&
  26. Looper.getMainLooper()!=Looper.myLooper()){
  27. Log.w(HardwareRenderer.LOG_TAG,"Attemptingtoinitializehardware"
  28. +"accelerationoutsideofthemainthread,aborting");
  29. return;
  30. }
  31. if(mAttachInfo.mHardwareRenderer!=null){
  32. mAttachInfo.mHardwareRenderer.destroy(true);
  33. }
  34. finalbooleantranslucent=attrs.format!=PixelFormat.OPAQUE;
  35. mAttachInfo.mHardwareRenderer=HardwareRenderer.createGlRenderer(2,translucent);
  36. if(mAttachInfo.mHardwareRenderer!=null){
  37. mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
  38. mAttachInfo.mHardwareAccelerated=
  39. mAttachInfo.mHardwareAccelerationRequested=true;
  40. }
  41. }elseif(fakeHwAccelerated){
  42. //Thewindowhadwantedtousehardwareacceleration,butthis
  43. //isnotallowedinitsprocess.Bysettingthisflag,itcan
  44. //stillrenderasifitwasaccelerated.Thisisbasicallyfor
  45. //thepreviewwindowsthewindowmanagershowsforlaunching
  46. //applications,sotheywilllookmoreliketheappbeinglaunched.
  47. mAttachInfo.mHardwareAccelerationRequested=true;
  48. }
  49. }
  50. }

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

                               [cpp] view plain copy                     
  1. staticHardwareRenderercreateGlRenderer(intglVersion,booleantranslucent){
  2. switch(glVersion){
  3. case2:
  4. returnGl20Renderer.create(translucent);
  5. }
  6. thrownewIllegalArgumentException("UnknownGLversion:"+glVersion);
  7. }
  8. staticHardwareRenderercreate(booleantranslucent){
  9. if(GLES20Canvas.isAvailable()){
  10. returnnewGl20Renderer(translucent);
  11. }
  12. returnnull;
  13. }
  14. GlRenderer(intglVersion,booleantranslucent){
  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. //Drawwithhardwarerenderer.
  3. mIsAnimating=false;
  4. mHardwareYOffset=yoff;
  5. mResizeAlpha=resizeAlpha;
  6. mCurrentDirty.set(dirty);
  7. dirty.setEmpty();
  8. attachInfo.mHardwareRenderer.draw(mView,attachInfo,this,
  9. animating?null:mCurrentDirty);
  10. }else{
  11. //Ifwegetherewithadisabled&requestedhardwarerenderer,somethingwent
  12. //wrong(aninvalidatepostedrightbeforewedestroyedthehardwaresurface
  13. //forinstance)soweshouldjustbailout.Lockingthesurfacewithsoftware
  14. //renderingatthispointwouldlockitforeverandpreventhardwarerenderer
  15. //fromdoingitsjobwhenitcomesback.
  16. //Beforewerequestanewframewemusthoweverattempttoreinitiliazethe
  17. //hardwarerendererifit'sinrequestedstate.Thiswouldhappenafteran
  18. //eglTerminate()forinstance.
  19. if(attachInfo.mHardwareRenderer!=null&&
  20. !attachInfo.mHardwareRenderer.isEnabled()&&
  21. attachInfo.mHardwareRenderer.isRequested()){
  22. try{
  23. attachInfo.mHardwareRenderer.initializeIfNeeded(mWidth,mHeight,
  24. mHolder.getSurface());
  25. }catch(OutOfResourcesExceptione){
  26. handleOutOfResourcesException(e);
  27. return;
  28. }
  29. mFullRedrawNeeded=true;
  30. scheduleTraversals();
  31. return;
  32. }
  33. if(!drawSoftware(surface,attachInfo,yoff,scalingRequired,dirty)){
  34. return;
  35. }
  36. }
  37. }

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


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

                               [cpp] view plain copy                       
  1. voiddraw(Viewview,View.AttachInfoattachInfo,HardwareDrawCallbackscallbacks,
  2. Rectdirty){
  3. if(canDraw()){
  4. if(surfaceState!=SURFACE_STATE_ERROR){
  5. HardwareCanvascanvas=mCanvas;
  6. attachInfo.mHardwareCanvas=canvas;
  7. dirty=beginFrame(canvas,dirty,surfaceState);
  8. DisplayListdisplayList=buildDisplayList(view,canvas);
  9. intsaveCount=0;
  10. intstatus=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. if(displayList!=null){
  19. status|=drawDisplayList(attachInfo,canvas,displayList,status);
  20. }
  21. }catch(Exceptione){
  22. Log.e(LOG_TAG,"Anerrorhasoccurredwhiledrawing:",e);
  23. }finally{
  24. callbacks.onHardwarePostDraw(canvas);
  25. canvas.restoreToCount(saveCount);
  26. view.mRecreateDisplayList=false;
  27. if(mDrawDelta>0){
  28. mFrameCount++;
  29. debugOverdraw(attachInfo,dirty,canvas,displayList);
  30. debugDirtyRegions(dirty,canvas);
  31. drawProfileData(attachInfo);
  32. }
  33. }
  34. onPostDraw();
  35. swapBuffers(status);
  36. }
  37. }
  38. }

上面这几十行代码,就是硬件绘制的最重要的几个流程调用,我们看到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函数实现:

                               [cpp] view plain copy                       
  1. staticvoidandroid_view_HardwareRenderer_beginFrame(JNIEnv*env,jobjectclazz,
  2. jintArraysize){
  3. EGLDisplaydisplay=eglGetCurrentDisplay();
  4. EGLSurfacesurface=eglGetCurrentSurface(EGL_DRAW);
  5. if(size){
  6. EGLintvalue;
  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. privateDisplayListbuildDisplayList(Viewview,HardwareCanvascanvas){
  2. if(mDrawDelta<=0){//如果根本没有新的绘制发生,使用之前的displaylist即可
  3. returnview.mDisplayList;
  4. }
  5. view.mRecreateDisplayList=(view.mPrivateFlags&View.PFLAG_INVALIDATED)
  6. ==View.PFLAG_INVALIDATED;
  7. view.mPrivateFlags&=~View.PFLAG_INVALIDATED;
  8. longbuildDisplayListStartTime=startBuildDisplayListProfiling();
  9. canvas.clearLayerUpdates();
  10. Trace.traceBegin(Trace.TRACE_TAG_VIEW,"getDisplayList");
  11. DisplayListdisplayList=view.getDisplayList();
  12. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  13. endBuildDisplayListProfiling(buildDisplayListStartTime);
  14. returndisplayList;
  15. }

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

                               [cpp] view plain copy                       
  1. privateDisplayListgetDisplayList(DisplayListdisplayList,booleanisLayer){
  2. if(((mPrivateFlags&PFLAG_DRAWING_CACHE_VALID)==0||
  3. displayList==null||!displayList.isValid()||
  4. (!isLayer&&mRecreateDisplayList))){
  5. //Don'tneedtorecreatethedisplaylist,justneedtotellour
  6. //childrentorestore/recreatetheirs
  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. returndisplayList;
  13. }
  14. if(!isLayer){
  15. //Ifwegothere,we'rerecreatingit.Markitassuchtoensurethat
  16. //wecopyinchilddisplaylistsintooursindrawChild()
  17. mRecreateDisplayList=true;
  18. }
  19. if(displayList==null){
  20. displayList=mAttachInfo.mHardwareRenderer.createDisplayList(getClass().getName());
  21. //Ifwe'recreatinganewdisplaylist,makesureourparentgetsinvalidated
  22. //sincetheywillneedtorecreatetheirdisplaylisttoaccountforthis
  23. //newchilddisplaylist.
  24. invalidateParentCaches();
  25. }
  26. booleancaching=false;
  27. intwidth=mRight-mLeft;
  28. intheight=mBottom-mTop;
  29. intlayerType=getLayerType();
  30. finalHardwareCanvascanvas=displayList.start(width,height);
  31. ...

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

2.1 createDisplayList

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

                               [cpp] view plain copy                       
  1. publicDisplayListcreateDisplayList(Stringname){
  2. returnnewGLES20DisplayList(name);
  3. }

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

2.2 invalidateParentCaches

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

                               [cpp] view plain copy                       
  1. protectedvoidinvalidateParentCaches(){
  2. if(mParentinstanceofView){
  3. ((View)mParent).mPrivateFlags|=PFLAG_INVALIDATED;
  4. }
  5. }

2.3 displayList.start

下面看下displayList.start函数:

                               [cpp] view plain copy                       
  1. publicHardwareCanvasstart(intwidth,intheight){
  2. mValid=false;
  3. mCanvas=GLES20RecordingCanvas.obtain(this);
  4. mCanvas.start();
  5. mCanvas.setViewport(width,height);
  6. //Thedirtyrectshouldalwaysbenullforadisplaylist
  7. mCanvas.onPreDraw(null);
  8. returnmCanvas;
  9. }

2.3.1 GLES20RecordingCanvas.obtain

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

                               [cpp] view plain copy                       
  1. staticGLES20RecordingCanvasobtain(GLES20DisplayListdisplayList){
  2. GLES20RecordingCanvascanvas=sPool.acquire();
  3. if(canvas==null){
  4. canvas=newGLES20RecordingCanvas();
  5. }
  6. canvas.mDisplayList=displayList;
  7. returncanvas;
  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. protectedGLES20Canvas(booleanrecord,booleantranslucent){
  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. staticOpenGLRenderer*android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv*env,
  2. jobjectclazz){
  3. returnnewDisplayListRenderer;
  4. }

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

                               [cpp] view plain copy                       
  1. classDisplayListData:publicLightRefBase<DisplayListData>{
  2. public:
  3. LinearAllocatorallocator;
  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. publicvoidsetViewport(intwidth,intheight){
  2. mWidth=width;
  3. mHeight=height;
  4. nSetViewport(mRenderer,width,height);
  5. }
  6. staticvoidandroid_view_GLES20Canvas_setViewport(JNIEnv*env,jobjectclazz,
  7. OpenGLRenderer*renderer,jintwidth,jintheight){
  8. renderer->setViewport(width,height);
  9. }
  10. voidDisplayListRenderer::setViewport(intwidth,intheight){
  11. mOrthoMatrix.loadOrtho(0,width,height,0,-1,1);
  12. mWidth=width;
  13. mHeight=height;
  14. }
  15. voidMatrix4::loadOrtho(floatleft,floatright,floatbottom,floattop,floatnear,floatfar){
  16. loadIdentity();
  17. data[kScaleX]=2.0f/(right-left);
  18. data[kScaleY]=2.0f/(top-bottom);
  19. data[kScaleZ]=-2.0f/(far-near);
  20. data[kTranslateX]=-(right+left)/(right-left);
  21. data[kTranslateY]=-(top+bottom)/(top-bottom);
  22. data[kTranslateZ]=-(far+near)/(far-near);
  23. mType=kTypeTranslate|kTypeScale|kTypeRectToRect;
  24. }

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

2.3.4 GLES20RecordingCanvas.onPreDraw

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

                               [cpp] view plain copy                       
  1. publicintonPreDraw(Rectdirty){
  2. if(dirty!=null){
  3. returnnPrepareDirty(mRenderer,dirty.left,dirty.top,dirty.right,dirty.bottom,
  4. mOpaque);
  5. }else{
  6. returnnPrepare(mRenderer,mOpaque);
  7. }
  8. }
  9. staticintandroid_view_GLES20Canvas_prepare(JNIEnv*env,jobjectclazz,
  10. OpenGLRenderer*renderer,jbooleanopaque){
  11. returnrenderer->prepare(opaque);
  12. }
  13. status_tOpenGLRenderer::prepare(boolopaque){
  14. returnprepareDirty(0.0f,0.0f,mWidth,mHeight,opaque);
  15. }
  16. status_tOpenGLRenderer::prepareDirty(floatleft,floattop,
  17. floatright,floatbottom,boolopaque){
  18. setupFrameState(left,top,right,bottom,opaque);
  19. //Layerrendererswillstarttheframeimmediately
  20. //Theframebufferrendererwillfirstdeferthedisplaylist
  21. //foreachlayerandwaituntilthefirstdrawingcommand
  22. //tostarttheframe
  23. if(mSnapshot->fbo==0){
  24. syncState();
  25. updateLayers();
  26. }else{
  27. returnstartFrame();
  28. }
  29. returnDrawGlInfo::kStatusDone;
  30. }

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

                               [cpp] view plain copy                       
  1. voidOpenGLRenderer::setupFrameState(floatleft,floattop,
  2. floatright,floatbottom,boolopaque){
  3. mCaches.clearGarbage();
  4. mOpaque=opaque;
  5. mSnapshot=newSnapshot(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. finalHardwareLayerlayer=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#2getHardwareLayer()isnotvalid.");
  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. Bitmapcache=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. //Fastpathforlayoutswithnobackgrounds
  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. //Fastpathforlayoutswithnobackgrounds
  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. publicvoiddraw(Canvascanvas){
  2. //Step1,drawthebackground,ifneeded
  3. intsaveCount;
  4. if(!dirtyOpaque){
  5. finalDrawablebackground=mBackground;
  6. if(background!=null){
  7. finalintscrollX=mScrollX;
  8. finalintscrollY=mScrollY;
  9. if(mBackgroundSizeChanged){
  10. background.setBounds(0,0,mRight-mLeft,mBottom-mTop);
  11. mBackgroundSizeChanged=false;
  12. }
  13. if((scrollX|scrollY)==0){
  14. background.draw(canvas);
  15. }else{
  16. canvas.translate(scrollX,scrollY);
  17. background.draw(canvas);
  18. canvas.translate(-scrollX,-scrollY);
  19. }
  20. }
  21. }
  22. //skipstep2&5ifpossible(commoncase)
  23. finalintviewFlags=mViewFlags;
  24. booleanhorizontalEdges=(viewFlags&FADING_EDGE_HORIZONTAL)!=0;
  25. booleanverticalEdges=(viewFlags&FADING_EDGE_VERTICAL)!=0;
  26. if(!verticalEdges&&!horizontalEdges){
  27. //Step3,drawthecontent
  28. if(!dirtyOpaque)onDraw(canvas);
  29. //Step4,drawthechildren
  30. dispatchDraw(canvas);
  31. //Step6,drawdecorations(scrollbars)
  32. onDrawScrollBars(canvas);
  33. if(mOverlay!=null&&!mOverlay.isEmpty()){
  34. mOverlay.getOverlayView().dispatchDraw(canvas);
  35. }
  36. //we'redone...
  37. return;
  38. }
  39. }

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. publicvoiddraw(Canvascanvas){
  2. booleandrawTrack=true;
  3. Rectr=getBounds();
  4. if(drawTrack){
  5. drawTrack(canvas,r,vertical);
  6. }
  7. if(drawThumb){
  8. intsize=vertical?r.height():r.width();
  9. intthickness=vertical?r.width():r.height();
  10. intlength=Math.round((float)size*extent/range);
  11. intoffset=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的真正执行了。

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. GitHub 标星 2.5K+!教你通过玩游戏的方式学习 VIM!
  3. 如何在后台运行Linux命令?
  4. No.11 使用firewall配置的防火墙策略的生效模式
  5. Cocos2d-x +Android(安卓)+ Windows 7环境配置
  6. Android直播开发之旅(15):libjpeg库的编译移植与使用
  7. android 模拟器和PC用sockets通讯
  8. Android应用开发笔记(12):Android应用的自动升级、更新模块的实现
  9. [置顶] 玩转pandaboard之linaro对于Android的编译上的一些优化

随机推荐

  1. Android之常用adb指令
  2. Android(安卓)Zip压缩解压缩
  3. android ubuntu /windows usb device lis
  4. Android的Activity动画切换
  5. Android时间获取之——Date、String、Lon
  6. Android实现widget定时更新
  7. android adb命令
  8. 在本地UI使用webview,在html页面用js与and
  9. 14天学会安卓开发(第十三天)Android多媒
  10. android开发新浪微博客户端 完整攻略 [新