您的位置:首页 > 移动开发 > Android开发

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是不是顶级的
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()没讲到,抱歉了。罪过罪过啊!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息