閱讀者三篇Android繪制文章,會讓你對理解Android繪制有幫助:
- Android Render(一)Activity窗口構(gòu)成和繪制解析
- 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)方法從表面看來就是接受的參數(shù)不一樣, 其實二期有一個先后順序,從屬關(guān)系,但是也要分情況,就看當(dāng)前的View是不是頂級的DecorView
了,就是說一個View有沒有parent view,View的兩個draw方法是有不一樣的調(diào)用順序的,當(dāng)然只有DecorView是頂級的View,DecorView沒有parent view。
正常情況下,draw(Canvas canvas, ViewGroup parent, long drawingTime)方法被當(dāng)前view的parentView調(diào)用,在draw(Canvas canvas, ViewGroup parent, long drawingTime)方法中會根據(jù)是否支持硬件加速來走不通的流程最終都會調(diào)用到
draw(Canvas canvas)
方法來做正在的繪制,draw(Canvas canvas)
方法中會調(diào)用到dispatchDraw(canvas)
方法,來向下分發(fā)繪制,dispatchDraw(canvas)
方法中會調(diào)用draw(Canvas canvas, ViewGroup parent, long drawingTime)。繪制就層層向下傳遞。
但是作為頂級的
DecorView
就不同了,ViewRootImpl
調(diào)用DecorView的draw(Canvas canvas)方法直接開啟整個view tree的繪制,DecorView的draw(Canvas canvas)方法中調(diào)用dispatchDraw(canvas)
開始向下分發(fā)繪制。一層層傳到到view tree的最底層。
整個View Hierarchy真正繪制開始是從DecorView的draw(Canvas canvas)方法開始的,下面描述一下Activity的啟動到調(diào)用到DecorView的draw(Canvas canvas)方法的流程:
/**1*/ ApplicationThread的onTransact方法接收到SystemServer進(jìn)程的SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION啟動Activity的Binder信息
↓
/**2*/ ApplicationThread.scheduleLaunchActivity() //
↓
/**3*/ ActivityThread.scheduleLaunchActivity() //安排啟動Activity
↓
/**4*/ ActivityThread.handleLaunchActivity() //處理啟動Activity
↓
/**5*/ ActivityThread.handleResumeActivity() // Activity 的Resume會使DecorView跟ViewRootImpl關(guān)聯(lián)
↓
/**6*/ WindowManagerGlobal.addView() //全局保存窗口的信息
↓
/**7*/ ViewRootImpl.setView() //使DecorView和ViewRootImpl關(guān)聯(lián)并繪制界面
↓
/**8*/ ViewRootImpl.requestLayout() //請求繪制ViewTree
↓
/**9*/ ViewRootImpl.scheduleTraversals() // 安排遍歷
↓
/**10*/ ViewRootImpl.doTraversal() //
↓
/**11*/ ViewRootImpl.performTraversals() //執(zhí)行遍歷 會根據(jù)情況調(diào)用relayoutWindow performMeasure performLayout performDraw 等方法 這四個方法跟繪制是緊密相關(guān)的
↓
/**12*/ ViewRootImpl.performDraw() //執(zhí)行繪制
↓
/**13*/ ViewRootImpl.draw(boolean fullRedrawNeeded)//區(qū)分是否支持硬件加速來走不同的繪制流程
↓
.........
↓
/**14*/ DecorView.draw(Canvas canvas) //不管走不走硬件加速都會調(diào)到這里
整個界面的繪制從 DecorView.draw(Canvas canvas)方法開始一觸即發(fā)!
一、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()調(diào)用的
* drawChild()方法又是被ViewGroup中的dispatchDraw(Canvas canvas)方法調(diào)用的
* 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.
*/
//判斷當(dāng)前View是否支持硬件加速繪制
boolean drawingWithRenderNode = mAttachInfo != null
&& mAttachInfo.mHardwareAccelerated
&& hardwareAcceleratedCanvas;
......略
//硬件加速繪制用到的繪制節(jié)點
RenderNode renderNode = null;
//cpu繪制用到的繪制緩存
Bitmap cache = null;
//獲取當(dāng)前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繪制緩存構(gòu)建
buildDrawingCache(true);
}
//獲得cpu繪制緩存結(jié)果 存儲在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繪制失敗標(biāo)識
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;
//沒有內(nèi)容不需要繪制自己,就直接向下分發(fā)繪制子View
dispatchDraw(canvas);
} else {
//繪制自己后再分發(fā)繪制子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,必要時,保存畫布的圖層為褪色做準(zhǔn)備
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, 繪制View自身內(nèi)容
if (!dirtyOpaque) onDraw(canvas);
//Step 4, 繪制子View的內(nèi)容
dispatchDraw(canvas);
// Overlay is part of the content and draws beneath Foreground
//Step 5, 必要時,繪制褪色邊緣并恢復(fù)圖層,通過 getOverlay().add(drawable); 添加圖片什么的
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, 繪制裝飾(列如滾動條)
onDrawForeground(canvas);
// we're done...
return;
}
......略
}
可以看到View中的 updateDisplayListIfDirty()
方法是gpu繪制的關(guān)鍵,buildDrawingCache()
方法是cpu繪制的關(guān)鍵。
updateDisplayListIfDirty()
和buildDrawingCache()
方法都會調(diào)用到View的draw(canvas)方法,但是updateDisplayListIfDirty()
方法中給draw(canvas)傳的是DisplayListCanvas參數(shù),使其具備HWUI的功能。buildDrawingCache()
方法中給draw(canvas)
方法傳入的是普通的Canvas。
也可以很清楚滴看到,對于我們開發(fā)者來說,draw(Canvas canvas, ViewGroup parent, long drawingTime)
方法就是一個View的繪制入口,從這個方法中決定了走cpu繪制還是gpu繪制。
draw(Canvas canvas)
方法是具體的繪制工作,如果是gpu硬件加速繪制,則使用DisplayListCanvas
畫布繪制,會把繪制DisplayList
保存在繪制節(jié)點RenderNode
中。如果是CPU軟繪制,則使用普通的Canvas
畫布繪制,把繪制緩存保存在一個Bitmap
中,最后會使用canvas.drawBitmap()
方法使用skia
渲染引擎cpu繪制緩存Bitmap
中的數(shù)據(jù)。
二、頂級DecorView硬件加速調(diào)用draw(canvas)
注意:
DecorView
其實是一個FrameLayout
,FrameLayout
是一個ViewGroup
,ViewGroup
是一個繼承View
的抽象類,draw(canvas)
方法只在View
類中有實現(xiàn),所以調(diào)用DecorView
的draw(canvas)
其實最終調(diào)用的是View
的draw(canvas)
方法。
上面已經(jīng)說了,DecorView
是頂級View,它的draw(canvas)
方法是繪制的開端,那么在硬件加速下ViewRootImpl是怎么調(diào)用到DecorView的draw(canvas)
的呢?
得從ViewRootImpl的draw(boolean fullRedrawNeeded)
方法開始分析:
/***********************************************************************
/**1*/ ApplicationThread的onTransact方法接收到SystemServer進(jìn)程的SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION啟動Activity的Binder信息
↓
/**2*/ ApplicationThread.scheduleLaunchActivity() //
↓
/**3*/ ActivityThread.scheduleLaunchActivity() //安排啟動Activity
↓
/**4*/ ActivityThread.handleLaunchActivity() //處理啟動Activity
↓
/**5*/ ActivityThread.handleResumeActivity() // Activity 的Resume會使DecorView跟ViewRootImpl關(guān)聯(lián)
↓
/**6*/ WindowManagerGlobal.addView() //全局保存窗口的信息
↓
/**7*/ ViewRootImpl.setView() //使DecorView和ViewRootImpl關(guān)聯(lián)并繪制界面
↓
/**8*/ ViewRootImpl.requestLayout() //請求繪制ViewTree
↓
/**9*/ ViewRootImpl.scheduleTraversals() // 安排遍歷
↓
/**10*/ ViewRootImpl.doTraversal() //
↓
/**11*/ ViewRootImpl.performTraversals() //執(zhí)行遍歷 會根據(jù)情況調(diào)用relayoutWindow performMeasure performLayout performDraw 等方法 這四個方法跟繪制是緊密相關(guān)的
↓
/**12*/ ViewRootImpl.performDraw() //執(zhí)行繪制
↓
/**13*/ 區(qū)分是否支持硬件加速來走不同的繪制流程*********************************/
private void draw(boolean fullRedrawNeeded) {
......略
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { //支持硬件加速并且需要繪制
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
......略
//1 硬件加速調(diào)DecorView 的draw(canvas)方法的關(guān)鍵
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
} else {
......略
//2 非硬件加速調(diào)DecorView的draw(canvas)方法的關(guān)鍵
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
......略
}
}
ThreadedRenderer
是5.0上為每個進(jìn)程新增了一個RenderThread
線程,既一個渲染線程,RenderThread
線程可以保證在主線程阻塞的情況下動畫執(zhí)行依然流暢順滑。就是一個異步繪制的處理線程。
更多請參看:
http://www.lxweimin.com/p/bc1c1d2fadd1
http://blog.csdn.net/guoqifa29/article/details/45131099
我們先分析硬件加速調(diào)用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()其實是調(diào)用的DecorView的updateDisplayListIfDirty方法,
//通過層層調(diào)用updateDisplayListIfDirty方法最終會獲取整個view tree的繪制節(jié)點`RenderNode`
canvas.drawRenderNode(view.updateDisplayListIfDirty());
canvas.insertInorderBarrier();
callbacks.onHardwarePostDraw(canvas);
canvas.restoreToCount(saveCount);
mRootNodeNeedsUpdate = false;
} finally {
//3 整個View tree繪制結(jié)束后回收資源
mRootNode.end(canvas);
}
}
}
......略
}
從上面可以看到 更新DecorView
的DisplayList
而調(diào)用 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而調(diào)用view.updateDisplayListIfDirty()方法
view.updateDisplayListIfDirty();
view.mRecreateDisplayList = false;
}
......略
}
看到這里那么可以知道,硬件加速的情況下,DecorView
的updateDisplayListIfDirty
方法是關(guān)鍵,也是從這里調(diào)用到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); //創(chuàng)建CPU繪制緩存會調(diào)用到View的
Bitmap cache = getDrawingCache(true); //保存CPU繪制緩存
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, mLayerPaint); //skia繪制收集到的Bitmap緩存數(shù)據(jù)
}
} 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本身不需要繪制 直接分發(fā)給子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繪制數(shù)據(jù)DisplayList的繪制節(jié)點renderNode
}
......略
}
可以看到在View的updateDisplayListIfDirty
方法中,支持硬件加速的情況下準(zhǔn)備好RenderNode
和DisplayListCanvas
后直接調(diào)用了View的draw(canvas)
方法。
總結(jié)流程:
//前提是需要支持硬件加速
ViewRootImpl.draw(boolean fullRedrawNeeded)
↓
ThreadedRenderer.draw(view,attachInfo,hardwareDrawCallbacks)
↓
ThreadedRenderer.updateRootDisplayList(view, callbacks)
↓
DecorView.updateDisplayListIfDirty()
↓
DecorView.draw(canvas)
三、頂級DecorView非硬件加速調(diào)用draw(canvas)
從上面的ViewRootImpl
的draw(boolean fullRedrawNeeded)
方法中可以看到,如果是CPU繪制,就會走drawSoftware()
方法。那么我們看一下drawSoftware()
中是怎么調(diào)到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);
......略
//調(diào)用DecorView的draw(canvas)方法
mView.draw(canvas);
......略
return true;
}
總結(jié)流程:
//前提是不支持硬件加速
ViewRootImpl.draw(boolean fullRedrawNeeded)
↓
ViewRootImpl.drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)
↓
DecorView.draw(canvas)
DecorView的draw(canvas)方法調(diào)用總結(jié):
DecorView
作為頂級View的存在,它的繪制是由ViewRootImpl
判斷是CPU還是GPU繪制然后調(diào)用DecorView
的draw(canvas)
方法,開啟整個界面的繪制。
其余的View,都有是自己的父控件調(diào)用
draw(canvas,parent,drawingTime)
方法,在draw(canvas,parent,drawingTime)方法中判斷當(dāng)前View是CPU還是GPU繪制然后調(diào)用draw(canvas)
。
四、非頂級View硬件加速draw(canvas, parent, drawingTime)調(diào)用draw(Canvas canvas)
上面我已經(jīng)說了,整個界面的繪制是從DecorView
的draw(canvas)
方法開始,普通View的繪制是從draw(canvas, parent, drawingTime)
開始。
View的draw(canvas, parent, drawingTime)、draw(Canvas canvas)和updateDisplayListIfDirty()
三個方法我這里就不粘貼了,上已經(jīng)有了,直接給出流程吧:
//GPU繪制
Vew.draw(Canvas canvas, ViewGroup parent, long drawingTime)
↓
Vew.updateDisplayListIfDirty()
↓
Vew.draw(displayListCanvas)
五、非頂級View非硬件加速draw(canvas, parent, drawingTime)調(diào)用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)方法調(diào)用到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 {
//執(zhí)行CPU繪制緩存創(chuàng)建
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為空就重新創(chuàng)建和賦值
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設(shè)置為我們創(chuàng)建的緩存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); //自己不需要繪制,直接分發(fā)子View繪制
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
} else {
//自己需要繪制,然后再分發(fā)子View繪制,所有的繪制都會畫在緩存Bitmap上面
draw(canvas);
}
......略
}
......略
}
繪制總結(jié):
不管是支持還是不支持硬件加速,都會調(diào)用到View的draw(canvas)
方法。
只是硬件加速的情況下為DisplayListCanvas
畫布,得到的DisplayList
數(shù)據(jù)保存在每一個View的繪制節(jié)點RenderNode
中,最后交給DisplayListCanvas
的drawRenderNode(renderNode)
方法處理渲染操作。
非硬件加速的情況下,會把所有的繪制緩存數(shù)據(jù)保存到一個緩存Bitmap
中,然后由Canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint)負(fù)責(zé)把數(shù)據(jù)交給skia
渲染。
DisplayList構(gòu)建分析請看:http://www.lxweimin.com/p/7bf306c09c7e
整體測量
、定位
、繪制
流程總結(jié)總結(jié):
其實draw
流程相對于Measure
和Layout
來說特殊一些,為什么這么說?如果你了解view的整個流程就知道,Measure
和Layout
流程在View
和ViewGroup
這兩個基本控件中都沒有具體實現(xiàn),而View的繪制在View這個基本的類中都實現(xiàn)了,而且在View
的raw(Canvas canvas, ViewGroup parent, long drawingTime) 方法中區(qū)分了cpu
和gpu
繪制來走不同的流程。View
的draw(Canvas canvas)方法實現(xiàn)了具體的6個繪制步驟,ViewGroup
中的dispatchDraw(Canvas canvas)方法實現(xiàn)了具體的子View的繪制分發(fā)。
為什么是這樣?
因為為基本的View
和ViewGroup
控件不能決定具體的樣子,這里說的樣子更多偏重于控件的排列,大小寬高,之間的相對位置,這些屬性都是由,Measure
和Layout
流程決定的。所以不管你怎么走Measure
和Layout
流程,其draw繪制都是一樣的。draw繪制出來的樣子是由具體的LinearLayout
、FrameLayout
、RelativeLayout
、RecyclerView
等等控件的Measure
和Layout
流程決定的。所以Measure
和Layout
流程需要在具體的控件中具體實現(xiàn)。
當(dāng)然上面說的是系統(tǒng)的控件,LinearLayout
、FrameLayout
這些,自定義的View
或者ViewGroup
的流程那就完全由開發(fā)者自己說了。
不清楚繪制流程的請看:http://blog.csdn.net/yanbober/article/details/46128379
硬件繪制軟件繪不足之處
目前硬件繪制軟件繪制都存在不足,比如(來自:http://blog.csdn.net/jxt1234and2010/article/details/47326411):
臟區(qū)域識別之后并沒有充分地優(yōu)化 。
軟件渲染時,盡管限制了渲染區(qū)域,但所有View
的onDraw
方法一個不丟的執(zhí)行了一遍。 硬件渲染時,避免了沒刷新的View
調(diào)onDraw
方法更新顯示列表,但顯示列表中的命令仍然一個不落的在全屏幕上執(zhí)行了一遍。
一個比較容易想到的優(yōu)化方案就是為主流程中的View建立一個R-Tree索引,invalidate
這一接口修改為可以傳入一個矩形范圍R,更新時,利用R-Tree索引找出包含R的所有葉子View,令這些View在R范圍重繪一次即可。
這個槽點其實影響倒不是很大,大部分情況下View不多,且如果出現(xiàn)性能問題,基本上都是一半以上的屏幕刷新。
對不住大家了,有一個硬件繪制很關(guān)鍵大方法dispatchGetDisplayList()沒講到,抱歉了。罪過罪過啊!