閱讀者三篇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.lxweimin.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.lxweimin.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()沒講到,抱歉了。罪過罪過啊!