当其第一次被使用时会调用如下函数:
一个ViewRootImpl对象对应了一个Surface对象,在其源码中有如下代码:
接下来就到了WindowSurfaceController.getSurface方法:
上面看到了一个IGraphicBufferProducergbp对象,这是一个很重要的对象,我们看一下它是怎么被创建的:
在Java层中ViewRootImpl实例中持有一个Surface对象,该Surface对象中的mNativeObject属性指向native层中创建的Surface对象,native层的Surface对应SurfaceFlinger中的Layer对象,它持有Layer中的BufferQueueProducer生产者指针,在后面的绘制过程中Surface会通过这个生产者来请求图形缓存区,在Surface上绘制的内容就是存入到这个缓存区里的,最终再交由SurfaceFlinger通过BufferQueueConsumer消费者取出这些缓存数据,并合成渲染送到显示器显示。
Resterization:栅格化(光栅化)。栅格化把Button,TextView等组件拆分到不同的像素上进行显示,这是一个很费时的操作,GPU可以加快栅格化的操作。
Android系统的UI从绘制到显示在屏幕上可分为两个步骤:
软件渲染
硬件渲染
判断是否支持硬件加速:
//如果Application、Activity配置了不开启硬件加速,则返回falseview.isHardwareAccelerated()//假如没有设置setLayerType,则受到Application、Activity的影响//假如设置了setLayerType,其返回值则受到setLayerType参数的影响canvas.isHardwareAccelerated()在绘制过程中会通过VRImpl.enableHardwareAcceleration方法去判断是否需要开启硬件加速:
privatebooleandrawSoftware(Surfacesurface,AttachInfoattachInfo,intxoff,intyoff,booleanscalingRequired,Rectdirty,RectsurfaceInsets){//Drawwithsoftwarerenderer.finalCanvascanvas;canvas=mSurface.lockCanvas(dirty);canvas.setDensity(mDensity);try{dirty.setEmpty();mView.draw(canvas);}finally{surface.unlockCanvasAndPost(canvas);}returntrue;}上面的软件绘制可以分成三个步骤:
publicCanvaslockCanvas(RectinOutDirty)throwsSurface.OutOfResourcesException,IllegalArgumentException{synchronized(mLock){if(mLockedObject!=0){//refusetore-locktheSurface.thrownewIllegalArgumentException("Surfacewasalreadylocked");}mLockedObject=nativeLockCanvas(mNativeObject,mCanvas,inOutDirty);returnmCanvas;}}这里调用native方法nativeLockCanvas来获取mLockedObject指针,上面说过mNativeObject指向native层创建的Surface对象。
小结:Surface.lockCanvas方法的作用是通过BufferQueueProducer生产者从BufferQueue队列中取出一个图形缓存区GraphicBuffer(用来创建Canvas中的Bitmap对象)并锁定该Surface,然后将Surface的地址返回给Java层Surface中的mLockedObject属性。
软件绘制可以简单分成以下三个步骤:
BufferQueueProducer中的两个重要函数:
对于软件绘制中的Canvas而言其绘制目标是一个Bitmap对象,绘制的内容会填充到Surface持有的缓存区(GraphicBuffer)里。
voiddraw(Viewview,AttachInfoattachInfo,DrawCallbackscallbacks,FrameDrawingCallbackframeDrawingCallback){finalChoreographerchoreographer=attachInfo.mViewRootImpl.mChoreographer;choreographer.mFrameInfo.markDrawStart();//构建View的DrawOp树updateRootDisplayList(view,callbacks);//通知RenderThread线程进行绘制intsyncResult=nSyncAndDrawFrame(mNativeProxy,frameInfo,frameInfo.length);//...}可以将硬件绘制分为两个阶段:构建阶段和渲染阶段。
publicvoidend(DisplayListCanvascanvas){longdisplayList=canvas.finishRecording();//将displayList缓存到native层的RenderNode中nSetDisplayList(mNativeRenderNode,displayList);canvas.recycle();}RenderNode.end方法用来将displayList缓存到native层的RenderNode中。在updateDisplayListIfDirty方法遍历了子View并将缓存了displayList的RenderNode返回后,ThreadedRenderer通过DisplayListCanvas.drawRenderNode方法将之前返回的RenderNode合入ThreadedRenderer内部的RenderNode中,然后也通过RenderNode.end方法将displayList缓存到native层的RenderNode里。
申请内存
软件绘制申请内存是通过Surface.lockCanvas方法借由BufferQueueProducer取出一个图形缓存区GraphicBuffer。至于硬件加速的内存是怎么申请的可以看看这部分代码(performTraversals方法应该很熟悉了):
可以看到硬件加速请求SurfaceFlinger内存分配的时机会比软件绘制更前,硬件加速这么设计可以预先分配内存,避免在渲染的时候再申请,防止分配内存失败时浪费了CPU之前的构建等工作,另外也可以将渲染线程的工作简化。
渲染线程绑定Surface
接着看一下Render线程是怎么跟目标Surface绘图界面绑定的(因为同一时刻可能有多个Surface绘图界面,它需要绑定一个渲染的上下文),从上面看到申请内存前调用了ThreadedRenderer.initialize方法:
渲染
当渲染线程绑定了Surface,且Surface内存分配以及DrawOp树构建完成后,便可以看一下渲染流程,从上面的nSyncAndDrawFrame方法开始,其实现在Native层:
硬件加速可以从两个阶段来看:
其中硬件加速的内存申请跟软件绘制一样都是借助Layer中的BufferQueueProducer生产者从BufferQueue中出队列一块空闲缓存区GraphicBuffer用来渲染数据的,之后也都会通知SurfaceFlinger进行合成。不一样的地方在于硬件加速相比软件绘制而言算法可能更加合理,同时采用了一个单独的Render线程,减轻了主线程的负担。
软件绘制
软件绘制可能会绘制到不需要重绘的视图,且其绘制过程在主线程进行的,可能会造成卡顿等情况。它把要绘制的内容写进一个Bitmap位图,其实就是填充到了Surface申请的图形缓存区里。
软件绘制可分为三个步骤:
硬件绘制
硬件绘制会将绘制函数作为绘制指令(DrawOp)记录在一个列表(DisplayList)中,然后交给单独的Render线程使用GPU进行硬件加速渲染。它只需要针对需要更新的View对象的脏区进行记录或更新,无需更新的View对象则能重用先前DisplayList中记录的指令。
硬件绘制可分为两个阶段:
硬件加速的内存申请跟软件绘制一样都是借助Layer中的BufferQueueProducer生产者从BufferQueue中出队列一块空闲缓存区GraphicBuffer用来渲染数据的,之后也都会通知SurfaceFlinger进行合成。不一样的地方在于硬件加速相比软件绘制而言算法可能更加合理,同时采用了一个单独的Render线程,减轻了主线程的负担。
源码解析中如有问题欢迎指出,因为有些地方的逻辑我也一知半解,争取查漏补缺修正。用一张图总结一下Android软硬件绘制的流程: