版權(quán)聲明:本文為博主原創(chuàng)文章冈在,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明按摘。
本文鏈接:http://www.reibang.com/p/4e00ccfd4d66
接著文章http://www.reibang.com/p/2dbe8f5df49e說起包券,本篇講View的Draw過程。Draw分為softwareDraw和HardwareRenderer(硬件加速)炫贤。
我們先講softwareDraw再講HardwareRenderer溅固。
從performDraw說起。
ViewRootImpl的performTraversals中刷新UI的時(shí)候兰珍,當(dāng)完成了View的measure和layout之后會(huì)對(duì)View進(jìn)行Draw侍郭。performTraversals會(huì)調(diào)用到performDraw開始執(zhí)行繪制操作,我們先看看performDraw的代碼俩垃。
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
}
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
// For whatever reason we didn't create a HardwareRenderer, end any
// hardware animations that are now dangling
if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
}
mAttachInfo.mPendingAnimatingRenderNodes.clear();
}
if (mReportNextDraw) {
mReportNextDraw = false;
// if we're using multi-thread renderer, wait for the window frame draws
if (mWindowDrawCountDown != null) {
try {
mWindowDrawCountDown.await();
} catch (InterruptedException e) {
Log.e(mTag, "Window redraw count down interruped!");
}
mWindowDrawCountDown = null;
}
if (mAttachInfo.mHardwareRenderer != null) {
mAttachInfo.mHardwareRenderer.fence();
mAttachInfo.mHardwareRenderer.setStopped(mStopped);
}
if (LOCAL_LOGV) {
Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
try {
mWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
}
performDraw的主要過程是調(diào)用draw励幼,draw主要是判斷如果支持硬件加速,則調(diào)用硬件加速draw口柳,否則調(diào)用software draw苹粟。那來看看draw。
private void draw(boolean fullRedrawNeeded) {
final Rect dirty = mDirty;
if (fullRedrawNeeded) {
mAttachInfo.mIgnoreDirtyState = true;
dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
mAttachInfo.mTreeObserver.dispatchOnDraw();
int xOffset = -mCanvasOffsetX;
int yOffset = -mCanvasOffsetY + curScrollY;
final WindowManager.LayoutParams params = mWindowAttributes;
final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
if (surfaceInsets != null) {
xOffset -= surfaceInsets.left;
yOffset -= surfaceInsets.top;
// Offset dirty rect for surface insets.
dirty.offset(surfaceInsets.left, surfaceInsets.right);
}
mAttachInfo.mDrawingTime =
mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
//根據(jù)!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty等判斷是否是否需要繪制
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
//支持硬件繪制則用HardwareRenderer
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
// If accessibility focus moved, always invalidate the root.
boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
mInvalidateRootRequested = false;
// Draw with hardware renderer.
mIsAnimating = false;
if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
mHardwareYOffset = yOffset;
mHardwareXOffset = xOffset;
invalidateRoot = true;
}
if (invalidateRoot) {
mAttachInfo.mHardwareRenderer.invalidateRoot();
}
dirty.setEmpty();
// Stage the content drawn size now. It will be transferred to the renderer
// shortly before the draw commands get send to the renderer.
final boolean updated = updateContentDrawBounds();
if (mReportNextDraw) {
// report next draw overrides setStopped()
// This value is re-sync'd to the value of mStopped
// in the handling of mReportNextDraw post-draw.
mAttachInfo.mHardwareRenderer.setStopped(false);
}
if (updated) {
requestDrawWindow();
}
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
} else {
//否則不支持硬件繪制跃闹,但是硬件繪制是需要的嵌削,嘗試初始化硬件繪制,成功之后調(diào)用scheduleTraversals()望艺,重新Traversals
if (mAttachInfo.mHardwareRenderer != null &&
!mAttachInfo.mHardwareRenderer.isEnabled() &&
mAttachInfo.mHardwareRenderer.isRequested()) {
try {
mAttachInfo.mHardwareRenderer.initializeIfNeeded(
mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
mFullRedrawNeeded = true;
scheduleTraversals();
return;
}
//否則不支持硬件繪制苛秕,同時(shí)硬件繪制不被需求,則drawSoftware
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
draw的邏輯比較簡(jiǎn)單找默,先判斷是否支持硬件繪制艇劫,如果支持。則調(diào)用硬件繪制mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this)惩激。如果不支持硬件繪制店煞,但是硬件繪制是需要的蟹演,則嘗試初始化硬件繪制,成功則從新Traversal顷蟀。否則進(jìn)行軟件繪制酒请。
drawSoftware也比較簡(jiǎn)單,通過mSurface得到Canvas鸣个,mSurface是ViewRootImpl·的Surface類型變量。這里我們先看看drawSoftware囤萤。
// These can be accessed by any thread, must be protected with a lock.
// Surface can never be reassigned or cleared (use Surface.clear()).
final Surface mSurface = new Surface();
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
final Canvas canvas;
try {
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
canvas = mSurface.lockCanvas(dirty);
// The dirty rectangle can be modified by Surface.lockCanvas()
//noinspection ConstantConditions
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
// TODO: Do this in native
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not lock surface", e);
// Don't assume this is due to out of memory, it could be
// something else, and if it is something else then we could
// kill stuff (or ourself) for no reason.
mLayoutRequested = true; // ask wm for a new surface next time.
return false;
}
try {
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
+ canvas.getWidth() + ", h=" + canvas.getHeight());
//canvas.drawARGB(255, 255, 0, 0);
}
// If this bitmap's format includes an alpha channel, we
// need to clear it before drawing so that the child will
// properly re-composite its drawing on a transparent
// background. This automatically respects the clip/dirty region
// or
// If we are applying an offset, we need to clear the area
// where the offset doesn't appear to avoid having garbage
// left in the blank areas.
if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
mView.mPrivateFlags |= View.PFLAG_DRAWN;
if (DEBUG_DRAW) {
Context cxt = mView.getContext();
Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
", metrics=" + cxt.getResources().getDisplayMetrics() +
", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
try {
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not unlock surface", e);
mLayoutRequested = true; // ask wm for a new surface next time.
//noinspection ReturnInsideFinallyBlock
return false;
}
if (LOCAL_LOGV) {
Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
}
}
return true;
}
核心就是調(diào)用View的draw昼窗,這個(gè)跟View的measure和layout調(diào)用一樣。最終會(huì)調(diào)用OnDraw阁将,進(jìn)行每個(gè)控件自身的Draw膏秫,我們自定義控件重繪時(shí)也是重寫OnDraw函數(shù)。但是View的draw又復(fù)雜點(diǎn)做盅,它會(huì)繪制View的背景和前景。同時(shí)如果有垂直或者水平邊需要繪制窘哈,又會(huì)繪制垂直或者水平邊吹榴。
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;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// we're done...
return;
}
//有垂直或者水平edge需要繪制的情況
/*
* Here we do the full fledged routine...
* (this is an uncommon case where speed matters less,
* this is why we repeat some of the tests that have been
* done above)
*/
boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;
float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;
// Step 2, save the canvas' layers
int paddingLeft = mPaddingLeft;
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
right += getRightPaddingOffset();
bottom += getBottomPaddingOffset();
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
// clip the fade length if top and bottom fades overlap
// overlapping fades produce odd-looking artifacts
if (verticalEdges && (top + length > bottom - length)) {
length = (bottom - top) / 2;
}
// also clip horizontal fades if necessary
if (horizontalEdges && (left + length > right - length)) {
length = (right - left) / 2;
}
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
drawTop = topFadeStrength * fadeHeight > 1.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
}
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
saveCount = canvas.getSaveCount();
int solidColor = getSolidColor();
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
if (drawBottom) {
canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
}
if (drawLeft) {
canvas.saveLayer(left, top, left + length, bottom, null, flags);
}
if (drawRight) {
canvas.saveLayer(right - length, top, right, bottom, null, flags);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, right, top + length, p);
}
if (drawBottom) {
matrix.setScale(1, fadeHeight * bottomFadeStrength);
matrix.postRotate(180);
matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, bottom - length, right, bottom, p);
}
if (drawLeft) {
matrix.setScale(1, fadeHeight * leftFadeStrength);
matrix.postRotate(-90);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, left + length, bottom, p);
}
if (drawRight) {
matrix.setScale(1, fadeHeight * rightFadeStrength);
matrix.postRotate(90);
matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(right - length, top, right, bottom, p);
}
canvas.restoreToCount(saveCount);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}
view的draw,注釋也寫的比較清楚滚婉,1:先繪制background图筹。2:如有必要繪制fading。3:調(diào)用onDraw繪制View自身的Content让腹。4:繪制children远剩。5:如有必要繪制fading edges。6:最后繪制Foreground骇窍。
在繪制背景和自身內(nèi)容的時(shí)候瓜晤,有一個(gè)條件dirtyOpaque,它的值為
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
所以設(shè)置PFLAG_DIRTY_OPAQUE屬性可以使dirtyOpaque為true腹纳,導(dǎo)致不用繪制背景和內(nèi)容痢掠,這對(duì)一些父布局設(shè)置設(shè)置是可以減少繪制次數(shù),提高繪制性能的嘲恍。
我們來看看drawBackground
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
if (background == null) {
return;
}
setBackgroundBounds();
// Attempt to use a display list if requested.
if (canvas.isHardwareAccelerated() && mAttachInfo != null
&& mAttachInfo.mHardwareRenderer != null) {
mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
final RenderNode renderNode = mBackgroundRenderNode;
if (renderNode != null && renderNode.isValid()) {
setBackgroundRenderNodeProperties(renderNode);
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
return;
}
}
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
先判斷canvas是否支持硬件繪制足画,如果支持,則獲取mBackgroundRenderNode佃牛,進(jìn)行硬件繪制淹辞。如果是canvas支持硬件繪制的canvas是DisplayListCanvas類型的對(duì)象,否則就是一般的canvas對(duì)象俘侠。
繪制自身的onDraw象缀,View中的定義是空的蔬将,每個(gè)控件集成View的時(shí)候會(huì)重寫它,實(shí)現(xiàn)自己的繪制攻冷。dispatchDraw這個(gè)主要是針對(duì)容器類控件循環(huán)調(diào)用子View的draw娃胆,又進(jìn)入了一個(gè)遞歸繪制過程。
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
注意此時(shí)調(diào)用的是View的另外一個(gè)draw函數(shù)等曼,它含有3個(gè)參數(shù)里烦,在其里面,它也會(huì)進(jìn)行一些是否支持硬件加速的判斷禁谦,如果支持則會(huì)調(diào)用updateDisplayListIfDirty更新顯示列表胁黑,否則會(huì)調(diào)用到draw(含一個(gè)canvas參數(shù)的)進(jìn)行繪制。
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.
*/
boolean drawingWithRenderNode = mAttachInfo != null
&& mAttachInfo.mHardwareAccelerated
&& hardwareAcceleratedCanvas;
boolean more = false;
final boolean childHasIdentityMatrix = hasIdentityMatrix();
final int parentFlags = parent.mGroupFlags;
RenderNode renderNode = null;
Bitmap cache = null;
int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local
if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {
if (layerType != LAYER_TYPE_NONE) {
// If not drawing with RenderNode, treat HW layers as SW
layerType = LAYER_TYPE_SOFTWARE;
buildDrawingCache(true);
}
cache = getDrawingCache(true);
}
//硬件繪制更新顯示列表updateDisplayListIfDirty
if (drawingWithRenderNode) {
// Delay getting the display list until animation-driven alpha values are
// set up and possibly passed on to the view
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;
drawingWithRenderNode = false;
}
}
//drawingWithDrawingCache不支持硬件繪制同時(shí)cache != null
final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode;
if (!drawingWithDrawingCache) {
if (drawingWithRenderNode) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
} else {
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
} else {
draw(canvas);
}
}
} else if (cache != null) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
if (layerType == LAYER_TYPE_NONE || mLayerPaint == null) {
// no layer paint, use temporary paint to draw bitmap
Paint cachePaint = parent.mCachePaint;
if (cachePaint == null) {
cachePaint = new Paint();
cachePaint.setDither(false);
parent.mCachePaint = cachePaint;
}
cachePaint.setAlpha((int) (alpha * 255));
canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
} else {
// use layer paint to draw the bitmap, merging the two alphas, but also restore
int layerPaintAlpha = mLayerPaint.getAlpha();
if (alpha < 1) {
mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
}
canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint);
if (alpha < 1) {
mLayerPaint.setAlpha(layerPaintAlpha);
}
}
}
mRecreateDisplayList = false;
return more;
}
上面就是draw(三個(gè)參數(shù))的主要過程州泊。支持硬件繪制則調(diào)用updateDisplayListIfDirty更新RenderNode丧蘸,然后Draw RenderNode。
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
software繪制遥皂,最終調(diào)用到draw(canvas)力喷。
以上就是繪制,主要是software繪制的大致過程演训。在這個(gè)過程中弟孟,我們也將了些硬件繪制的東西,比如updateDisplayListIfDirty样悟,drawRenderNode等拂募,那么接下來我們就來講講硬件繪制。
硬件繪制
我們又從ViewRootImpl的draw(private void draw(boolean fullRedrawNeeded))函數(shù)開始說起窟她〕轮ⅲ回到上文ViewRootImpl的draw中的硬件繪制調(diào)用
private void draw(boolean fullRedrawNeeded) {
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
// If accessibility focus moved, always invalidate the root.
boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
mInvalidateRootRequested = false;
// Draw with hardware renderer.
mIsAnimating = false;
if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
mHardwareYOffset = yOffset;
mHardwareXOffset = xOffset;
invalidateRoot = true;
}
if (invalidateRoot) {
mAttachInfo.mHardwareRenderer.invalidateRoot();
}
dirty.setEmpty();
// Stage the content drawn size now. It will be transferred to the renderer
// shortly before the draw commands get send to the renderer.
final boolean updated = updateContentDrawBounds();
if (mReportNextDraw) {
// report next draw overrides setStopped()
// This value is re-sync'd to the value of mStopped
// in the handling of mReportNextDraw post-draw.
mAttachInfo.mHardwareRenderer.setStopped(false);
}
if (updated) {
requestDrawWindow();
}
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
}
}
可以看到主要是調(diào)用了mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this),mAttachInfo是一個(gè)View.AttachInfo對(duì)象震糖,mHardwareRenderer是什么呢录肯,它是ThreadedRenderer 。還記得ViewRootImpl的的構(gòu)造函數(shù)中有一段代碼嗎试伙?它就是在這里構(gòu)造的嘁信。
if (mSurfaceHolder == null) {
enableHardwareAcceleration(attrs);
}
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;
mAttachInfo.mHardwareRenderer = ThreadedRenderer.create(mContext, translucent);
}
可以看到調(diào)用了ThreadedRenderer的靜態(tài)方法create創(chuàng)建的
public static ThreadedRenderer create(Context context, boolean translucent) {
ThreadedRenderer renderer = null;
if (DisplayListCanvas.isAvailable()) {
renderer = new ThreadedRenderer(context, translucent);
}
return renderer;
}
再來看看ThreadedRenderer
private long mNativeProxy;//native的Proxy代理
private RenderNode mRootNode;//根RenderNode
ThreadedRenderer(Context context, boolean translucent) {
final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
mAmbientShadowAlpha =
(int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
a.recycle();
long rootNodePtr = nCreateRootRenderNode();
mRootNode = RenderNode.adopt(rootNodePtr);
mRootNode.setClipToBounds(false);
mNativeProxy = nCreateProxy(translucent, rootNodePtr);
ProcessInitializer.sInstance.init(context, mNativeProxy);
loadSystemProperties();
}
那我們?cè)趤砜纯碦enderNode
public class RenderNode {
private boolean mValid;
// Do not access directly unless you are ThreadedRenderer
final long mNativeRenderNode;
private final View mOwningView;
private RenderNode(String name, View owningView) {
mNativeRenderNode = nCreate(name);
mOwningView = owningView;
if (mOwningView instanceof SurfaceView) {
nRequestPositionUpdates(mNativeRenderNode, (SurfaceView) mOwningView);
}
}
/**
* @see RenderNode#adopt(long)
*/
private RenderNode(long nativePtr) {
mNativeRenderNode = nativePtr;
mOwningView = null;
}
/**
* Creates a new RenderNode that can be used to record batches of
* drawing operations, and store / apply render properties when drawn.
*
* @param name The name of the RenderNode, used for debugging purpose. May be null.
*
* @return A new RenderNode.
*/
public static RenderNode create(String name, @Nullable View owningView) {
return new RenderNode(name, owningView);
}
/**
* Adopts an existing native render node.
*
* Note: This will *NOT* incRef() on the native object, however it will
* decRef() when it is destroyed. The caller should have already incRef'd it
*/
public static RenderNode adopt(long nativePtr) {
return new RenderNode(nativePtr);
}
}
還有一個(gè)DisplayListCanvas,它繼承Canvas疏叨。
···
public class DisplayListCanvas extends Canvas
···
硬件渲染java層涉及的類大概就這幾個(gè)潘靖,它們的關(guān)系是ThreadedRenderer負(fù)責(zé)draw。draw什么呢蚤蔓。draw的是RenderNode卦溢。或者Text或者Circle,Bitmap等单寂。而又在哪里draw呢贬芥,在DisplayListCanvas中draw。每個(gè)View都有一個(gè)RenderNode宣决,它在View的構(gòu)造函數(shù)中初始化蘸劈。而每個(gè)RenderNode又會(huì)和一個(gè)DisplayListCanvas關(guān)聯(lián)。以上就是java層的相關(guān)類尊沸。
final RenderNode mRenderNode;
public View(Context context) {
mContext = context;
mResources = context != null ? context.getResources() : null;
mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
mRenderNode = RenderNode.create(getClass().getName(), this);
}
View() {
mResources = null;
mRenderNode = RenderNode.create(getClass().getName(), this);
}
//RenderNode的start函數(shù)
public DisplayListCanvas start(int width, int height) {
return DisplayListCanvas.obtain(this, width, height);
}
這樣我們明白了每個(gè)View有一個(gè)RenderNode威沫,每個(gè)RenderNode會(huì)獲取到一個(gè)對(duì)應(yīng)的DisplayListCanvas。而我們是在DisplayListCanvas上draw的洼专。所以對(duì)一個(gè)View的Draw來說棒掠。在之前的draw中也說過draw分為6部分
public void draw(Canvas canvas) {
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
}
那就是在DisplayListCanvas依次draw了6個(gè)部分。比如background是一個(gè)bitmap或者是一個(gè)RenderNode屁商。Content烟很,我們可以舉例假設(shè)是一個(gè)Circle。Draw children蜡镶。child是一個(gè)View或者多個(gè)View雾袱。每個(gè)View會(huì)對(duì)應(yīng)一個(gè)RenderNode,假設(shè)只有一個(gè)View官还,那就是Draw了一個(gè)RenderNode谜酒。這樣View的RenderNode樹形圖我們可以理解成這樣。
下面看看Native層妻枕,Native層比較重要的和DisplayListCanvas對(duì)應(yīng)的類有Canvas,RecordingCanvas類粘驰,RecordingCanvas繼承Canvas屡谐。在DisplayListCanvas中調(diào)的繪制,比如繪制Circle或者RenderNode等蝌数,最終都是調(diào)用到RecordingCanvas中去愕掏。
來看看Native層的RecordingCanvas和它的drawRenderNode以及drawCircle
class ANDROID_API RecordingCanvas: public Canvas, public CanvasStateClient {
enum class DeferredBarrierType {
None,
InOrder,
OutOfOrder,
};
public:
RecordingCanvas(size_t width, size_t height);
void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
auto&& stagingProps = renderNode->stagingProperties();
RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
Rect(stagingProps.getWidth(), stagingProps.getHeight()),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
renderNode);
int opIndex = addOp(op);
if (CC_LIKELY(opIndex >= 0)) {
int childIndex = mDisplayList->addChild(op);
// update the chunk's child indices
DisplayList::Chunk& chunk = mDisplayList->chunks.back();
chunk.endChildIndex = childIndex + 1;
if (renderNode->stagingProperties().isProjectionReceiver()) {
// use staging property, since recording on UI thread
mDisplayList->projectionReceiveIndex = opIndex;
}
}
}
void RecordingCanvas::drawCircle(
CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
mDisplayList->ref(x);
mDisplayList->ref(y);
mDisplayList->ref(radius);
mDisplayList->ref(paint);
refBitmapsInShader(paint->value.getShader());
addOp(alloc().create_trivial<CirclePropsOp>(
*(mState.currentSnapshot()->transform),
getRecordedClip(),
&paint->value,
&x->value, &y->value, &radius->value));
}
可以看到無論是drawCircle還是drawRenderNode。到了Native層顶伞,最終是形成一個(gè)CirclePropsOp或者RenderNodeOp饵撑。然后將其加入到mDisplayList中。
看看CirclePropsOp唆貌,RenderNodeOp等滑潘,他們都繼承RecordedOp
struct CirclePropsOp : RecordedOp {
CirclePropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint,
float* x, float* y, float* radius)
: RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClip, paint)
, x(x)
, y(y)
, radius(radius) {}
const float* x;
const float* y;
const float* radius;
};
struct RenderNodeOp : RecordedOp {
RenderNodeOp(BASE_PARAMS_PAINTLESS, RenderNode* renderNode)
: SUPER_PAINTLESS(RenderNodeOp)
, renderNode(renderNode) {}
RenderNode * renderNode; // not const, since drawing modifies it
/**
* Holds the transformation between the projection surface ViewGroup and this RenderNode
* drawing instance. Represents any translations / transformations done within the drawing of
* the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
* DisplayList draw instance.
*
* Note: doesn't include transformation within the RenderNode, or its properties.
*/
Matrix4 transformFromCompositingAncestor;
bool skipInOrderDraw = false;
};
包括其它的如BitmapOp,BitmapMeshOp锨咙,BitmapRectOp语卤,LinesOp(參考frameworks\base\libs\hwui\RecordOp.h文件)等等都是繼承RecordedOp。至此,我們明白了所有的這一切粹舵,無論是draw circle還是draw line钮孵,bitmap,RecordNode等眼滤,最終是形成一個(gè)RecordedOp巴席,然后add到RecordingCanvas的mDisplayList中∽缧瑁看看mDisplayList漾唉。
DisplayList* mDisplayList = nullptr;
typedef RecordedOp BaseOpType;
typedef RenderNodeOp NodeOpType;
class DisplayList {
friend class RecordingCanvas;
public:
struct Chunk {
// range of included ops in DisplayList::ops()
size_t beginOpIndex;
size_t endOpIndex;
// range of included children in DisplayList::children()
size_t beginChildIndex;
size_t endChildIndex;
// whether children with non-zero Z in the chunk should be reordered
bool reorderChildren;
// clip at the beginning of a reorder section, applied to reordered children
const ClipBase* reorderClip;
};
DisplayList();
virtual ~DisplayList();
LsaVector<BaseOpType*> ops;
結(jié)合java層的圖,我們最終可以理解成這樣诱担,所有的操作最終在LsaVector<BaseOpType*> ops中增加一條RecordedOp毡证。接下來我們來看看DisplayList是如何更新的
更新顯示列表
調(diào)用
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
updateRootDisplayList(view, callbacks);
}
private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
updateViewTreeDisplayList(view);
}
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;
view.updateDisplayListIfDirty();
view.mRecreateDisplayList = false;
}
最終調(diào)用到了View的updateDisplayListIfDirty,這個(gè)我們?cè)谇懊嬷v過蔫仙,現(xiàn)在系統(tǒng)梳理一遍料睛。
public RenderNode updateDisplayListIfDirty() {
final RenderNode renderNode = mRenderNode;
try {
if (layerType == LAYER_TYPE_SOFTWARE) {
buildDrawingCache(true);
Bitmap cache = getDrawingCache(true);
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, mLayerPaint);
}
} else {
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);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
}
}
} finally {
renderNode.end(canvas);
setDisplayListProperties(renderNode);
}
} else {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
return renderNode;
}
關(guān)鍵過程是通過View自身的mRenderNode變量,通過調(diào)用start函數(shù)摇邦,獲得一個(gè)DisplayListCanvas canvas恤煞,然后將canvas作為參數(shù)傳給draw,進(jìn)行繪制施籍。
final DisplayListCanvas canvas = renderNode.start(width, height);
public DisplayListCanvas start(int width, int height) {
return DisplayListCanvas.obtain(this, width, height);
}
所以在draw中就有draw背景居扒,draw Content,Children等丑慎。而draw Children的時(shí)候調(diào)用的是dispatchDraw(canvas)喜喂,注意參數(shù)是canvas,這個(gè)canvas是父View的RenderNode對(duì)應(yīng)的Canvas竿裂,而dispatchDraw中最終會(huì)調(diào)用到drawChild玉吁,drawChild又調(diào)用到了child.draw(三個(gè)參數(shù),第一個(gè)為Canvas腻异,為父View的canvas)
protected void dispatchDraw(Canvas canvas) {
for (int i = 0; i < childrenCount; i++) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
所以這里就進(jìn)入到了子View的draw(三個(gè)參數(shù))进副,同時(shí)參數(shù)傳遞的Canvas是父View的。
來看看draw(三個(gè)參數(shù))的主要過程
//此時(shí)進(jìn)入了子View的draw調(diào)用悔常,參數(shù)canvas為父View的
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.
*/
boolean drawingWithRenderNode = mAttachInfo != null
&& mAttachInfo.mHardwareAccelerated
&& hardwareAcceleratedCanvas;
boolean more = false;
final boolean childHasIdentityMatrix = hasIdentityMatrix();
final int parentFlags = parent.mGroupFlags;
if (drawingWithRenderNode) {
// Delay getting the display list until animation-driven alpha values are
// set up and possibly passed on to the view
//關(guān)鍵步驟1:遞歸調(diào)用updateDisplayListIfDirty更新自身顯示列表影斑,將自身的內(nèi)容draw在
//自身的Canvas上
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;
drawingWithRenderNode = false;
}
}
if (!drawingWithDrawingCache) {
if (drawingWithRenderNode) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
//關(guān)鍵步驟2:將自身的RenderNode和父View的Canvas關(guān)聯(lián)
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
} else {
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
} else {
draw(canvas);
}
}
}
上面注釋已經(jīng)寫的比較清楚了,draw(三個(gè))時(shí)遞歸調(diào)用updateDisplayListIfDirty更新自身顯示列表返回RenderNode机打,然后將返回的RenderNode draw在父View的Canvas上矫户,這樣就一層層完成了顯示列表的更新。
最后回到ThreadedRenderer的updateRootDisplayList姐帚,將跟View吏垮,即DecorView的RenderNode draw在ThreadedRenderer的canvas上
private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
updateViewTreeDisplayList(view);
if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
try {
final int saveCount = canvas.save();
canvas.translate(mInsetLeft, mInsetTop);
callbacks.onHardwarePreDraw(canvas);
canvas.insertReorderBarrier();
canvas.drawRenderNode(view.updateDisplayListIfDirty());
canvas.insertInorderBarrier();
callbacks.onHardwarePostDraw(canvas);
canvas.restoreToCount(saveCount);
mRootNodeNeedsUpdate = false;
} finally {
mRootNode.end(canvas);
}
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
這樣就完成了顯示列表的更新障涯,最后又回到mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this)
void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
attachInfo.mIgnoreDirtyState = true;
final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
choreographer.mFrameInfo.markDrawStart();
//更新顯示列表
updateRootDisplayList(view, callbacks);
attachInfo.mIgnoreDirtyState = false;
// register animating rendernodes which started animating prior to renderer
// creation, which is typical for animators started prior to first draw
if (attachInfo.mPendingAnimatingRenderNodes != null) {
final int count = attachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
registerAnimatingRenderNode(
attachInfo.mPendingAnimatingRenderNodes.get(i));
}
attachInfo.mPendingAnimatingRenderNodes.clear();
// We don't need this anymore as subsequent calls to
// ViewRootImpl#attachRenderNodeAnimator will go directly to us.
attachInfo.mPendingAnimatingRenderNodes = null;
}
//更新完顯示列表之后調(diào)用nSyncAndDrawFrame進(jìn)行渲染
final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
setEnabled(false);
attachInfo.mViewRootImpl.mSurface.release();
// Invalidate since we failed to draw. This should fetch a Surface
// if it is still needed or do nothing if we are no longer drawing
attachInfo.mViewRootImpl.invalidate();
}
if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
attachInfo.mViewRootImpl.invalidate();
}
}
至此終于講完了java層UI繪制的相關(guān)內(nèi)容和過程,但是具體的Native層的繪制膳汪,調(diào)用nSyncAndDrawFrame到底如何繪制的唯蝶,后面繼續(xù)。