Android Render(三)7.1源码硬件加速下draw绘制流程分析
2017-11-01 17:22
483 查看
阅读者三篇Android绘制文章,会让你对理解Android绘制有帮助:
- Android Render(一)Activity窗口构成和绘制解析
- Android Render(二)7.1源码硬件加速下draw绘制流程分析
- Android Render(三)supportVersion 27.0.0源码RecyclerView绘制流程解析
分析从draw(Canvas canvas)和draw(Canvas canvas, ViewGroup parent, long drawingTime)两个方法入手:
draw(Canvas canvas)和draw(Canvas canvas, ViewGroup parent, long drawingTime)方法从表面看来就是接受的参数不一样, 其实二期有一个先后顺序,从属关系,但是也要分情况,就看当前的View是不是顶级的
正常情况下,draw(Canvas canvas, ViewGroup parent, long drawingTime)方法被当前view的parentView调用,在draw(Canvas canvas, ViewGroup parent, long drawingTime)方法中会根据是否支持硬件加速来走不通的流程最终都会调用到
但是作为顶级的
整个View Hierarchy真正绘制开始是从DecorView的draw(Canvas canvas)方法开始的,下面描述一下Activity的启动到调用到DecorView的draw(Canvas canvas)方法的流程:
整个界面的绘制从 DecorView.draw(Canvas canvas)方法开始一触即发!
可以看到View中的
也可以很清楚滴看到,对于我们开发者来说,
上面已经说了,
得从ViewRootImpl的
更多请参看:
http://www.jianshu.com/p/bc1c1d2fadd1
http://blog.csdn.net/guoqifa29/article/details/45131099
我们先分析硬件加速调用
从上面可以看到 更新
看到这里那么可以知道,硬件加速的情况下,
可以看到在View的
其余的View,都有是自己的父控件调用
View的draw(canvas, parent, drawingTime)、draw(Canvas canvas)和
draw(canvas, parent, drawingTime)方法调用到
只是硬件加速的情况下为
非硬件加速的情况下,会把所有的绘制缓存数据保存到一个缓存
DisplayList构建分析请看:http://www.jianshu.com/p/7bf306c09c7e
整体
其实
为什么是这样?
因为为基本的
当然上面说的是系统的控件,
不清楚绘制流程的请看:http://blog.csdn.net/yanbober/article/details/46128379
脏区域识别之后并没有充分地优化 。
软件渲染时,尽管限制了渲染区域,但所有
硬件渲染时,避免了没刷新的
一个比较容易想到的优化方案就是为主流程中的View建立一个R-Tree索引,
这个槽点其实影响倒不是很大,大部分情况下View不多,且如果出现性能问题,基本上都是一半以上的屏幕刷新。
对不住大家了,有一个硬件绘制很关键大方法dispatchGetDisplayList()没讲到,抱歉了。罪过罪过啊!
- Android Render(一)Activity窗口构成和绘制解析
- Android Render(二)7.1源码硬件加速下draw绘制流程分析
- Android Render(三)supportVersion 27.0.0源码RecyclerView绘制流程解析
分析从draw(Canvas canvas)和draw(Canvas canvas, ViewGroup parent, long drawingTime)两个方法入手:
draw(Canvas canvas)和draw(Canvas canvas, ViewGroup parent, long drawingTime)方法从表面看来就是接受的参数不一样, 其实二期有一个先后顺序,从属关系,但是也要分情况,就看当前的View是不是顶级的
DecorView了,就是说一个View有没有parent view,View的两个draw方法是有不一样的调用顺序的,当然只有DecorView是顶级的View,DecorView没有parent view。
正常情况下,draw(Canvas canvas, ViewGroup parent, long drawingTime)方法被当前view的parentView调用,在draw(Canvas canvas, ViewGroup parent, long drawingTime)方法中会根据是否支持硬件加速来走不通的流程最终都会调用到
draw(Canvas canvas)方法来做正在的绘制,
draw(Canvas canvas)方法中会调用到
dispatchDraw(canvas)方法,来向下分发绘制,
dispatchDraw(canvas)方法中会调用draw(Canvas canvas, ViewGroup parent, long drawingTime)。绘制就层层向下传递。
但是作为顶级的
DecorView就不同了,
ViewRootImpl调用DecorView的draw(Canvas canvas)方法直接开启整个view tree的绘制,DecorView的draw(Canvas canvas)方法中调用
dispatchDraw(canvas)开始向下分发绘制。一层层传到到view tree的最底层。
整个View Hierarchy真正绘制开始是从DecorView的draw(Canvas canvas)方法开始的,下面描述一下Activity的启动到调用到DecorView的draw(Canvas canvas)方法的流程:
/**1*/ ApplicationThread的onTransact方法接收到SystemServer进程的SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION启动Activity的Binder信息 ↓ /**2*/ ApplicationThread.scheduleLaunchActivity() // ↓ /**3*/ ActivityThread.scheduleLaunchActivity() //安排启动Activity ↓ /**4*/ ActivityThread.handleLaunchActivity() //处理启动Activity ↓ /**5*/ ActivityThread.handleResumeActivity() // Activity 的Resume会使DecorView跟ViewRootImpl关联 ↓ /**6*/ WindowManagerGlobal.addView() //全局保存窗口的信息 ↓ /**7*/ ViewRootImpl.setView() //使DecorView和ViewRootImpl关联并绘制界面 ↓ /**8*/ ViewRootImpl.requestLayout() //请求绘制ViewTree ↓ /**9*/ ViewRootImpl.scheduleTraversals() // 安排遍历 ↓ /**10*/ ViewRootImpl.doTraversal() // ↓ /**11*/ ViewRootImpl.performTraversals() //执行遍历 会根据情况调用relayoutWindow performMeasure performLayout performDraw 等方法 这四个方法跟绘制是紧密相关的 ↓ /**12*/ ViewRootImpl.performDraw() //执行绘制 ↓ /**13*/ ViewRootImpl.draw(boolean fullRedrawNeeded)//区分是否支持硬件加速来走不同的绘制流程 ↓ ......... ↓ /**14*/ DecorView.draw(Canvas canvas) //不管走不走硬件加速都会调到这里
整个界面的绘制从 DecorView.draw(Canvas canvas)方法开始一触即发!
一、draw(canvas,parent,drawingTime)和draw(canvas)作用不同
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { /** * This method is called by ViewGroup.drawChild() to have each child view draw itself. * 此方法是被父控件ViewGroup.drawChild()调用的 * drawChild()方法又是被ViewGroup中的dispatchDraw(Canvas canvas)方法调用的 * This is where the View specializes rendering behavior based on layer type, * and hardware acceleration. */ boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) { final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated(); /* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList. * * If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't * HW accelerated, it can't handle drawing RenderNodes. */ //判断当前View是否支持硬件加速绘制 boolean drawingWithRenderNode = mAttachInfo != null && mAttachInfo.mHardwareAccelerated && hardwareAcceleratedCanvas; ......略 //硬件加速绘制用到的绘制节点 RenderNode renderNode = null; //cpu绘制用到的绘制缓存 Bitmap cache = null; //获取当前View的绘制类型 LAYER_TYPE_NONE LAYER_TYPE_SOFTWARE LAYER_TYPE_HARDWARE int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) { //如果是cpu绘制类型 if (layerType != LAYER_TYPE_NONE) { // If not drawing with RenderNode, treat HW layers as SW layerType = LAYER_TYPE_SOFTWARE; //开始cpu绘制缓存构建 buildDrawingCache(true); } //获得cpu绘制缓存结果 存储在Bitmap中 cache = getDrawingCache(true); } if (drawingWithRenderNode) { //支持硬件加速 // Delay getting the display list until animation-driven alpha values are // set up and possibly passed on to the view //更新gpu绘制列表 保存在RenderNode中 renderNode = updateDisplayListIfDirty(); if (!renderNode.isValid()) { // Uncommon, but possible. If a view is removed from the hierarchy during the call // to getDisplayList(), the display list will be marked invalid and we should not // try to use it again. renderNode = null; //gpu绘制失败标识 drawingWithRenderNode = false; } } ......略 //cpu绘制成功并且gpu绘制失败了 final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode; ......略 if (!drawingWithDrawingCache) { //走gpu绘制 if (drawingWithRenderNode) { //支持gpu绘制 mPrivateFlags &= ~PFLAG_DIRTY_MASK; //gpu绘制收集到的DisplayList ((DisplayListCanvas) canvas).drawRenderNode(renderNode); } else { //走gpu绘制又突然不支持gpu绘制(可能是极限情况下) // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; //没有内容不需要绘制自己,就直接向下分发绘制子View dispatchDraw(canvas); } else { //绘制自己后再分发绘制子View draw(canvas); } } } else if (cache != null) { //走cpu绘制且cpu绘制缓存不为null ......略 //把存储cpu绘制缓存的Bitmap用canvas走cpu绘制(skia渲染引擎) canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint); } ......略 return more; } /** * Manually render this view (and all of its children) to the given Canvas. * The view must have already done a full layout before this function is * called. When implementing a view, implement * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method. * If you do need to override this method, call the superclass version. * * @param canvas The Canvas to which the View is rendered. */ @CallSuper public void draw(Canvas canvas) { final int privateFlags = mPrivateFlags; final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE && (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState); mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; int saveCount; // Step 1 绘制背景 if (!dirtyOpaque) { drawBackground(canvas); } //Step 2,必要时,保存画布的图层为褪色做准备 final int viewFlags = mViewFlags; boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0; boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0; if (!verticalEdges && !horizontalEdges) { // Step 3, 绘制View自身内容 if (!dirtyOpaque) onDraw(canvas); //Step 4, 绘制子View的内容 dispatchDraw(canvas); // Overlay is part of the content and draws beneath Foreground //Step 5, 必要时,绘制褪色边缘并恢复图层,通过 getOverlay().add(drawable); 添加图片什么的 if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); } // Step 6, 绘制装饰(列如滚动条) onDrawForeground(canvas); // we're done... return; } ......略 }
可以看到View中的
updateDisplayListIfDirty()方法是gpu绘制的关键,
buildDrawingCache()方法是cpu绘制的关键。
updateDisplayListIfDirty()和
buildDrawingCache()方法都会调用到View的draw(canvas)方法,但是
updateDisplayListIfDirty()方法中给draw(canvas)传的是DisplayListCanvas参数,使其具备HWUI的功能。
buildDrawingCache()方法中给
draw(canvas)方法传入的是普通的Canvas。
也可以很清楚滴看到,对于我们开发者来说,
draw(Canvas canvas, ViewGroup parent, long drawingTime)方法就是一个View的绘制入口,从这个方法中决定了走cpu绘制还是gpu绘制。
draw(Canvas canvas)方法是具体的绘制工作,如果是gpu硬件加速绘制,则使用
DisplayListCanvas画布绘制,会把绘制
DisplayList保存在绘制节点
RenderNode中。如果是CPU软绘制,则使用普通的
Canvas画布绘制,把绘制缓存保存在一个
Bitmap中,最后会使用
canvas.drawBitmap()方法使用
skia渲染引擎cpu绘制缓存
Bitmap中的数据。
二、顶级DecorView硬件加速调用draw(canvas)
注意:DecorView其实是一个
FrameLayout,
FrameLayout是一个
ViewGroup,
ViewGroup是一个继承
View的抽象类,
draw(canvas)方法只在
View类中有实现,所以调用
DecorView的
draw(canvas)其实最终调用的是
View的
draw(canvas)方法。
上面已经说了,
DecorView是顶级View,它的
draw(canvas)方法是绘制的开端,那么在硬件加速下ViewRootImpl是怎么调用到DecorView的
draw(canvas)的呢?
得从ViewRootImpl的
draw(boolean fullRedrawNeeded)方法开始分析:
/*********************************************************************** /**1*/ ApplicationThread的onTransact方法接收到SystemServer进程的SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION启动Activity的Binder信息 ↓ /**2*/ ApplicationThread.scheduleLaunchActivity() // ↓ /**3*/ ActivityThread.scheduleLaunchActivity() //安排启动Activity ↓ /**4*/ ActivityThread.handleLaunchActivity() //处理启动Activity ↓ /**5*/ ActivityThread.handleResumeActivity() // Activity 的Resume会使DecorView跟ViewRootImpl关联 ↓ /**6*/ WindowManagerGlobal.addView() //全局保存窗口的信息 ↓ /**7*/ ViewRootImpl.setView() //使DecorView和ViewRootImpl关联并绘制界面 ↓ /**8*/ ViewRootImpl.requestLayout() //请求绘制ViewTree ↓ /**9*/ ViewRootImpl.scheduleTraversals() // 安排遍历 ↓ /**10*/ ViewRootImpl.doTraversal() // ↓ /**11*/ ViewRootImpl.performTraversals() //执行遍历 会根据情况调用relayoutWindow performMeasure performLayout performDraw 等方法 这四个方法跟绘制是紧密相关的 ↓ /**12*/ ViewRootImpl.performDraw() //执行绘制 ↓ /**13*/ 区分是否支持硬件加速来走不同的绘制流程*********************************/ private void draw(boolean fullRedrawNeeded) { ......略 if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { //支持硬件加速并且需要绘制 if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { ......略 //1 硬件加速调DecorView 的draw(canvas)方法的关键 mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this); } else { ......略 //2 非硬件加速调DecorView的draw(canvas)方法的关键 if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) { return; } } } ......略 } }
ThreadedRenderer是5.0上为每个进程新增了一个
RenderThread线程,既一个渲染线程,
RenderThread线程可以保证在主线程阻塞的情况下动画执行依然流畅顺滑。就是一个异步绘制的处理线程。
更多请参看:
http://www.jianshu.com/p/bc1c1d2fadd1
http://blog.csdn.net/guoqifa29/article/details/45131099
我们先分析硬件加速调用
DecorView的
draw(canvas)方法,先看
mAttachInfo.
mHardwareRenderer.draw(mView, mAttachInfo, this)里面的流程:
/** *5.0新增的渲染线程 */ public final class ThreadedRenderer { ......略 /** * Draws the specified view. * * @param view The view to draw. * @param attachInfo AttachInfo tied to the specified view. * @param callbacks Callbacks invoked when drawing happens. */ void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) { //1 圈起来 终点 要考的 ,其实就是更新`DecorView`的`DisplayList` updateRootDisplayList(view, callbacks); } //其实就是更新`DecorView`的`DisplayList` private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) { //更新`DecorView`的`DisplayList` updateViewTreeDisplayList(view); if (mRootNodeNeedsUpdate || !mRootNode.isValid()) { //1 获取一个DisplayListCanvas画布 DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight); try { final int saveCount = canvas.save(); canvas.translate(mInsetLeft, mInsetTop); callbacks.onHardwarePreDraw(canvas); canvas.insertReorderBarrier(); //2 绘制获取到的DecorView的RenderNode //view.updateDisplayListIfDirty()其实是调用的DecorView的updateDisplayListIfDirty方法, //通过层层调用updateDisplayListIfDirty方法最终会获取整个view tree的绘制节点`RenderNode` canvas.drawRenderNode(view.updateDisplayListIfDirty()); canvas.insertInorderBarrier(); callbacks.onHardwarePostDraw(canvas); canvas.restoreToCount(saveCount); mRootNodeNeedsUpdate = false; } finally { //3 整个View tree绘制结束后回收资源 mRootNode.end(canvas); } } } ......略 }
从上面可以看到 更新
DecorView的
DisplayList而调用 updateViewTreeDisplayList(view)方法,这个方法请看:
/** *5.0新增的渲染线程 */ public final class ThreadedRenderer { ......略 private void updateViewTreeDisplayList(View view) { view.mPrivateFlags |= View.PFLAG_DRAWN; view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) == View.PFLAG_INVALIDATED; view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; //其实也是更新DecorView的DisplayList而调用view.updateDisplayListIfDirty()方法 view.updateDisplayListIfDirty(); view.mRecreateDisplayList = false; } ......略 }
看到这里那么可以知道,硬件加速的情况下,
DecorView的
updateDisplayListIfDirty方法是关键,也是从这里调用到
DecorView的的draw(canvas)方法开启绘制的,请看源代码:
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { ......略 /** * 更新一个View的绘制DisplayList保存在RenderNode返回 * 返回的RenderNode是被ThreadedRenderer线程的drawRenderNode(RenderNode)方法绘制的 * Gets the RenderNode for the view, and updates its DisplayList (if needed and supported) * @hide */ @NonNull public RenderNode updateDisplayListIfDirty() { final RenderNode renderNode = mRenderNode; ......略 //从renderNode中获取一个DisplayListCanvas final DisplayListCanvas canvas = renderNode.start(width, height); canvas.setHighContrastText(mAttachInfo.mHighContrastText); try { if (layerType == LAYER_TYPE_SOFTWARE) { //为CPU绘制draw(canvas)方法 buildDrawingCache(true); //创建CPU绘制缓存会调用到View的 Bitmap cache = getDrawingCache(true); //保存CPU绘制缓存 if (cache != null) { canvas.drawBitmap(cache, 0, 0, mLayerPaint); //skia绘制收集到的Bitmap缓存数据 } } else { //为硬件加速GPU绘制 computeScroll(); canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { dispatchDraw(canvas); //View本身不需要绘制 直接分发给子View绘制 if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } } else { //使用DisplayListCanvas绘制,需要的绘制会保存在DisplayList draw(canvas); } } } finally { renderNode.end(canvas); //回收资源 setDisplayListProperties(renderNode); } } else { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; } return renderNode; //返回保存有GPU绘制数据DisplayList的绘制节点renderNode } ......略 }
可以看到在View的
updateDisplayListIfDirty方法中,支持硬件加速的情况下准备好
RenderNode和
DisplayListCanvas后直接调用了View的
draw(canvas)方法。
总结流程:
//前提是需要支持硬件加速 ViewRootImpl.draw(boolean fullRedrawNeeded) ↓ ThreadedRenderer.draw(view,attachInfo,hardwareDrawCallbacks) ↓ ThreadedRenderer.updateRootDisplayList(view, callbacks) ↓ DecorView.updateDisplayListIfDirty() ↓ DecorView.draw(canvas)
三、顶级DecorView非硬件加速调用draw(canvas)
从上面的ViewRootImpl的
draw(boolean fullRedrawNeeded)方法中可以看到,如果是CPU绘制,就会走
drawSoftware()方法。那么我们看一下
drawSoftware()中是怎么调到DecorView的
draw(canvas)方法的:
ViewRootImpl``drawSoftware()方法:
/** * @return true if drawing was successful, false if an error occurred */ private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty) { // Draw with software renderer. final Canvas canvas; ......略 //从Surface中获取一个普通的Canvas canvas = mSurface.lockCanvas(dirty); ......略 //调用DecorView的draw(canvas)方法 mView.draw(canvas); ......略 return true; }
总结流程:
//前提是不支持硬件加速 ViewRootImpl.draw(boolean fullRedrawNeeded) ↓ ViewRootImpl.drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty) ↓ DecorView.draw(canvas)
DecorView的draw(canvas)方法调用总结:
DecorView作为顶级View的存在,它的绘制是由
ViewRootImpl判断是CPU还是GPU绘制然后调用
DecorView的
draw(canvas)方法,开启整个界面的绘制。
其余的View,都有是自己的父控件调用
draw(canvas,parent,drawingTime)方法,在draw(canvas,parent,drawingTime)方法中判断当前View是CPU还是GPU绘制然后调用
draw(canvas)。
四、非顶级View硬件加速draw(canvas, parent, drawingTime)调用draw(Canvas canvas)
上面我已经说了,整个界面的绘制是从DecorView的
draw(canvas)方法开始,普通View的绘制是从
draw(canvas, parent, drawingTime)开始。
View的draw(canvas, parent, drawingTime)、draw(Canvas canvas)和
updateDisplayListIfDirty()三个方法我这里就不粘贴了,上已经有了,直接给出流程吧:
//GPU绘制 Vew.draw(Canvas canvas, ViewGroup parent, long drawingTime) ↓ Vew.updateDisplayListIfDirty() ↓ Vew.draw(displayListCanvas)
五、非顶级View非硬件加速draw(canvas, parent, drawingTime)调用draw(Canvas canvas)
先给出CPU绘制流程://CPU绘制 Vew.draw(Canvas canvas, ViewGroup parent, long drawingTime) ↓ Vew.buildDrawingCache(boolean autoScale) ↓ Vew.buildDrawingCacheImpl(boolean autoScale) ↓ Vew.draw(displayListCanvas)
draw(canvas, parent, drawingTime)方法调用到
buildDrawingCache在上面的代码中可以看到,这里就看一下
buildDrawingCache和
buildDrawingCacheImpl方法:
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { ......略 public void buildDrawingCache(boolean autoScale) { if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ? mDrawingCache == null : mUnscaledDrawingCache == null)) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "buildDrawingCache/SW Layer for " + getClass().getSimpleName()); } try { //执行CPU绘制缓存创建 buildDrawingCacheImpl(autoScale); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } } ......略 /** * private, internal implementation of buildDrawingCache, used to enable tracing */ private void buildDrawingCacheImpl(boolean autoScale) { ......略 //保存CPU绘制缓存的Bitmap Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache; ......略 if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { ......略 try { //如果缓存Bitmap为空就重新创建和赋值 bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(), width, height, quality); bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); if (autoScale) { mDrawingCache = bitmap; } else { mUnscaledDrawingCache = bitmap; } if (opaque && use32BitCache) bitmap.setHasAlpha(false); } catch (OutOfMemoryError e) { ......略 } clear = drawingCacheBackgroundColor != 0; } Canvas canvas; if (attachInfo != null) { //处理Canvas canvas = attachInfo.mCanvas; if (canvas == null) { canvas = new Canvas(); } //canvas的Bitmap设置为我们创建的缓存Bitmap canvas.setBitmap(bitmap); ......略 } else { // This case should hopefully never or seldom happen canvas = new Canvas(bitmap); } ......略 // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); //自己不需要绘制,直接分发子View绘制 if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } } else { //自己需要绘制,然后再分发子View绘制,所有的绘制都会画在缓存Bitmap上面 draw(canvas); } ......略 } ......略 }
绘制总结:
不管是支持还是不支持硬件加速,都会调用到View的draw(canvas)方法。
只是硬件加速的情况下为
DisplayListCanvas画布,得到的
DisplayList数据保存在每一个View的绘制节点
RenderNode中,最后交给
DisplayListCanvas的
drawRenderNode(renderNode)方法处理渲染操作。
非硬件加速的情况下,会把所有的绘制缓存数据保存到一个缓存
Bitmap中,然后由Canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint)负责把数据交给
skia渲染。
DisplayList构建分析请看:http://www.jianshu.com/p/7bf306c09c7e
整体测量
、定位
、绘制
流程总结总结:
其实draw流程相对于
Measure和
Layout来说特殊一些,为什么这么说?如果你了解view的整个流程就知道,
Measure和
Layout流程在
View和
ViewGroup这两个基本控件中都没有具体实现,而View的绘制在View这个基本的类中都实现了,而且在
View的raw(Canvas canvas, ViewGroup parent, long drawingTime) 方法中区分了
cpu和
gpu绘制来走不同的流程。
View的draw(Canvas canvas)方法实现了具体的6个绘制步骤,
ViewGroup中的dispatchDraw(Canvas canvas)方法实现了具体的子View的绘制分发。
为什么是这样?
因为为基本的
View和
ViewGroup控件不能决定具体的样子,这里说的样子更多偏重于控件的排列,大小宽高,之间的相对位置,这些属性都是由,
Measure和
Layout流程决定的。所以不管你怎么走
Measure和
Layout流程,其draw绘制都是一样的。draw绘制出来的样子是由具体的
LinearLayout、
FrameLayout、
RelativeLayout、
RecyclerView等等控件的
Measure和
Layout流程决定的。所以
Measure和
Layout流程需要在具体的控件中具体实现。
当然上面说的是系统的控件,
LinearLayout、
FrameLayout这些,自定义的
View或者
ViewGroup的流程那就完全由开发者自己说了。
不清楚绘制流程的请看:http://blog.csdn.net/yanbober/article/details/46128379
硬件绘制软件绘不足之处
目前硬件绘制软件绘制都存在不足,比如(来自:http://blog.csdn.net/jxt1234and2010/article/details/47326411):脏区域识别之后并没有充分地优化 。
软件渲染时,尽管限制了渲染区域,但所有
View的
onDraw方法一个不丢的执行了一遍。
硬件渲染时,避免了没刷新的
View调
onDraw方法更新显示列表,但显示列表中的命令仍然一个不落的在全屏幕上执行了一遍。
一个比较容易想到的优化方案就是为主流程中的View建立一个R-Tree索引,
invalidate这一接口修改为可以传入一个矩形范围R,更新时,利用R-Tree索引找出包含R的所有叶子View,令这些View在R范围重绘一次即可。
这个槽点其实影响倒不是很大,大部分情况下View不多,且如果出现性能问题,基本上都是一半以上的屏幕刷新。
对不住大家了,有一个硬件绘制很关键大方法dispatchGetDisplayList()没讲到,抱歉了。罪过罪过啊!
相关文章推荐
- 源码分析篇 - Android绘制流程(二)measure、layout、draw流程
- Android应用层View绘制流程与源码分析
- AndroidView绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- 源码分析-从ActivityThread到View绘制流程
- Struts2源码分析(三) 绘制Struts2执行的核心流程时序图并分析
- Android应用层View绘制流程与源码分析
- chromium for android v38硬件绘制渲染结构及流程分析(render进程)
- Android应用层View绘制流程与源码分析
- 源码分析android的UI绘制流程
- Android应用层View绘制流程与源码分析
- Activity的绘制流程简单分析(基于android 4.0源码进行分析)
- [置顶] DecorView绘制流程源码分析
- Android笔记--View绘制流程源码分析(一)
- Android视图View绘制流程及源码分析
- 从源码分析View绘制流程
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android笔记--View绘制流程源码分析(二)