寫在前面
作為一名android開發(fā)人員接觸學(xué)習(xí)flutter已有小半年了,無論是widget配置構(gòu)建響應(yīng)式ui的思想,dart vm hotReload的快捷,還是各種devtools帶來的開發(fā)便利都讓我體會(huì)到flutter不同于其他跨平臺(tái)框架的強(qiáng)大的魅力。深入了解了flutter的渲染流程才大致明白了flutter宣稱的媲美原生的流暢度是如何做到的。下面對(duì)比著android原生看下flutter framework是如何做ui渲染的。
UI渲染
Android 上無論是原生應(yīng)用,視頻解碼播放還是 FLutter 渲染都離不開Surface。UI 渲染本質(zhì)上就是圖像的生產(chǎn)者生產(chǎn)圖像流,圖像的消費(fèi)方消費(fèi)圖像流,由于兩端處于不同進(jìn)程有不同的運(yùn)行速度兩者數(shù)據(jù)傳遞就需要用到緩沖區(qū),在安卓上這個(gè)緩沖區(qū)對(duì)應(yīng)的就是BufferQueue,Surface是一個(gè)接口,供生產(chǎn)方與使用方交換緩沖區(qū)。下圖展示了生產(chǎn)者消費(fèi)者通過對(duì)緩沖區(qū)的操作實(shí)現(xiàn)數(shù)據(jù)的傳遞。
一個(gè) android 應(yīng)用一個(gè)典型的渲染流程就是:當(dāng) app 進(jìn)入前臺(tái)時(shí),WindowManager服務(wù)會(huì)向SurfaceFlinger請(qǐng)求一個(gè)繪圖Surface。SurfaceFlinger會(huì)創(chuàng)建一個(gè)其主要組件為BufferQueue的層,而SurfaceFlinger是其消耗方。生產(chǎn)方端的Binder對(duì)象通過WindowManager傳遞到應(yīng)用,然后應(yīng)用可以開始直接將幀發(fā)送到SurfaceFlinger。應(yīng)用通過 Vysnc 觸發(fā)繪制流程,經(jīng)過measure , layout, draw等流程最后通過柵格化把代碼表示的view 結(jié)構(gòu)對(duì)應(yīng)的display list轉(zhuǎn)化為像素?cái)?shù)據(jù)(支持硬件加速柵格化會(huì)通過 opengl es 調(diào)用 gpu 執(zhí)行,不持支的通過 skia 在 cpu 中執(zhí)行,現(xiàn)在的手機(jī)都有硬件加速了所以文章后面都是圍繞著有硬件加速展開的),隨后提交到BufferQueue中等待消費(fèi)。大多數(shù)應(yīng)用在屏幕上一次顯示三個(gè)層:屏幕頂部的狀態(tài)欄、底部或側(cè)面的導(dǎo)航欄以及應(yīng)用界面,SurfaceFlingers收到所有層后會(huì)和Hardware Composer一起完成層的合成工作最后交給顯示屏顯示。
Flutter 是使用SurfaceView依附于 android 原生之上,他的繪制流程不同的地方是在于Vysnc 觸發(fā)后進(jìn)行自己的animate, build, layout, paint等步驟根據(jù)我們構(gòu)建的widget tree生成的rendering tree轉(zhuǎn)化為一系列繪制指令最后根據(jù)指令柵格化為像素?cái)?shù)據(jù)。也就是說更換了生產(chǎn)圖片流的方式,視頻播放也是一樣不過生產(chǎn)方是用對(duì)應(yīng)解碼器從視頻文件流中解碼出一幀楨圖片像素?cái)?shù)據(jù)。
總的來說 android/flutter 渲染就是要把我們代碼里寫的 view/widget 對(duì)應(yīng)的 ui 結(jié)構(gòu)樹轉(zhuǎn)化為繪制指令集,再經(jīng)過 gpu 或者 cpu 柵格化轉(zhuǎn)化為像素?cái)?shù)據(jù)用于顯示。
開始渲染!
我所理解的 UI 渲染一般分為Event,Layout,Draw三個(gè)階段:
- Event:用戶做了某些操作或收到系統(tǒng)某些指令觸發(fā)繪制流程,如我們點(diǎn)擊按鈕,修改控件顯示文本等等。
- Layout:繪制之前對(duì)控件樹進(jìn)行尺寸測(cè)量和位置布局等。
- Draw:更新或重制繪制指令集,格柵化,顯示等
Android原生
在android中渲染都是由 Vysnc 驅(qū)動(dòng)的。Vysnc 信號(hào)由硬件產(chǎn)生經(jīng)過SurfaceFlinger轉(zhuǎn)發(fā)到應(yīng)用線程的Choreographer中,通過 ui 線程的 handler 切換線程調(diào)用doFrame方法開始每一幀的渲染流程:
void doFrame(long frameTimeNanos, int frame) {
...
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
...
}
該方法會(huì)依次處理4個(gè) callback
- INPUT:輸入事件
- ANIMATION:動(dòng)畫
- TRAVERSAL:窗口刷新,執(zhí)行 measure/layout/draw 操作
- COMMIT:遍歷完成的提交操作,用來修正動(dòng)畫啟動(dòng)時(shí)間
渲染主要看的是TRAVERSAL,這個(gè) callback 是由ViewRootImpl添加的,對(duì)應(yīng)的是ViewRootImpl中的TraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
doTraversal最終會(huì)調(diào)用performTraversals方法
private void performTraversals() {
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw();
}
該方法中會(huì)依次調(diào)用performMeasure/Layout/Draw方法開始繪制流程。
Event 階段
Chorepgrapher并不會(huì)一直接收到Vsync信號(hào)進(jìn)行重繪,只有一定的事件發(fā)生才會(huì)去接收信號(hào)這樣避免了無用的渲染。在Event階段要做的就是告訴系統(tǒng)我需要在下一幀重繪,下一個(gè)Vsync信號(hào)到來時(shí)才會(huì)觸發(fā)Choreographer的doFrame方法。
看一個(gè)簡(jiǎn)單的例子
最外層是DecorView,他是所有View流程的起點(diǎn)。navigationbarBackgorund和statusBarBackground就像前面所說的一樣不屬于app層,是由 systemui 渲染后 和 app 的層合并顯示的。
當(dāng)我們調(diào)用setText重新設(shè)置TextView顯示文字
根據(jù)調(diào)用??梢园l(fā)現(xiàn)settext后會(huì)分別調(diào)用requestLayout和invalidateInternal方法
public void requestLayout() {
...
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
...
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,boolean fullInvalidate) {
...
if (...) {
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
...
}
}
這2個(gè)方法基本都是對(duì) view 的mPrivateFlags標(biāo)志位進(jìn)行標(biāo)記,會(huì)標(biāo)記這個(gè)view及其父類,子類是否需要重新布局和重新繪制。
繼續(xù)往里走發(fā)現(xiàn)調(diào)用了DisplayEventReceiver的nativeScheduleVsync方法,往下就是C++層的代碼,后面的具體作用就是通過binder和SurfaceFlinger通信,告訴 SurfaceFlinger 我需要在下一幀重繪,當(dāng)硬件發(fā)送Vsync消息給SurfaceFlinger后SurfaceFlinger會(huì)通知應(yīng)用,最后就到了Chorepgrapher的doFrame方法開始繪制流程。完成了Event階段到Layout階段的切換。
Layout 階段
Android原生上這個(gè)階段對(duì)應(yīng)的就是 View 的 Measure 和 Layout 階段。
- measure 確定 view 的測(cè)量寬/高(mMeasureWidth/mMeasureHeight)
- layout 確定 view 的最終寬/高和四個(gè)頂點(diǎn)位置(mLeft,mTop,mRight.mBottom)
Measure
measure要分view和viewgroup兩種情況,view通過measure完成自己的測(cè)量,viewgroup除了完成自己的測(cè)量外還要遍歷調(diào)用所有子view的measure方法,各個(gè)child view/viewgroup 遞歸去執(zhí)行這個(gè)流程。
View measure
先看下View的measure方法:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
...
final boolean needsLayout = specChanged
&& (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
...
if (forceLayout || needsLayout) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
...
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
...
}
- 參數(shù)widthMeasureSpec,heightMeasureSpec,是 parent view 根據(jù) child view 的LayoutParam 和自身邏輯對(duì) view 的尺寸要求,如要求指定尺寸或者最大尺寸等等
- forceLayout:這里可以看到是對(duì)mPrivateFlags標(biāo)志位PFLAG_FORCE_LAYOUT的判斷,其實(shí)這個(gè)PFLAG_FORCE_LAYOUT就是Event階段我們?cè)?strong>requestLayout方法時(shí)候設(shè)置的,
- onMeasure方法:forcelayout為true或者滿足一些條件觸發(fā),如parent view傳的measureSpec有變換等等
- 設(shè)置PFLAG_LAYOUT_REQUIRED標(biāo)志位用于后面的layout方法
onMeasure方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
onMeasure方法大部分子類都會(huì)重寫,靠自己的規(guī)則測(cè)量出view的大?。ㄈ鏣extView會(huì)根據(jù)文字的排版確定尺寸),如果沒有重寫就比較簡(jiǎn)單根據(jù)背景圖寬高和設(shè)置的最小寬高屬性和measureSpec確定最終的測(cè)量大小并給mMeasuredWidth/mMeasuredHeight(這里只是測(cè)量尺寸,具體的尺寸還要layout后才能確定,但是絕大分測(cè)量尺寸就是view的最終尺寸,getMeasureWidth/getWidth)賦值,單個(gè)view的measure流程就完成了。
ViewGroup measure
再看看擁有子view的ViewGroup的measure流程,由于measure方法是final的子view并不能重寫,所以measure方法都是一樣的,所以直接從onMeasure開始看,這里以FramLayout為例:
onMeasure主要做2件事
- 傳遞尺寸要求調(diào)用所有child view的measure方法確定所有view的大小(每個(gè)child view又會(huì)調(diào)用自己的onMeasure方法測(cè)量自己的)
- 根據(jù)子view的最大寬高來調(diào)用setMeaureDimension來確定FrameLayout的寬高
onMeasure會(huì)遍歷所有child view并把child view 作為參數(shù)調(diào)用到ViewGroup 的 measureChildWithMargins方法:
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
可以看到根據(jù)getChildMeasureSpec方法生成childWidthMeasureSpec/childHeightMeasureSpec作為參數(shù)調(diào)用child view的measure方法開始遞歸完成所有view樹所有view的measure方法。
(getChildMeasureSpec方法參數(shù)傳遞了LayoutParam的width/height,也就是上文說說的 parent view 根據(jù) child view 的LayoutParam 和自身邏輯生成對(duì) view 的尺寸要求 measureSpec)
所有measure的起點(diǎn)則是上文所說的doFrame后調(diào)用的ViewRootImpl的performMeasure方法
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
...
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
mView其實(shí)就是DecorView,DecorView就是一個(gè)FramLayout,就是從它開始整個(gè)measure流程。
Layout
Layout的作用是確定view的位置,通過調(diào)用view的layout方法來確定該view的位置,layout會(huì)調(diào)用onLayout,view通過重寫onLayout方法就可以響應(yīng)view位置的變化,如果是ViewGroup還要負(fù)責(zé)在onLayout中遍歷所有的子元素并調(diào)用其layout方法種當(dāng)ViewGroup的位置被確定后,它 onLayout 中會(huì)遍歷所有的子元素并調(diào)用其layout方法。完成所有view 的layout。
先看下layout方法
public void layout(int l, int t, int r, int b) {
...
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
...
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
...
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
}
...
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
...
}
- l,t,r,b 就是parent view對(duì)這個(gè)view確定的位置
- 調(diào)用setFrame方法對(duì)比定點(diǎn)位置和原來的位置有沒有變化
- 如果頂點(diǎn)位置有變化或者設(shè)置PFLAG_LAYOUT_REQUIRED標(biāo)志位(measure方法里設(shè)置的)觸發(fā)onLayout方法
- mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED, mPrivateFlags &= ~PFLAG_FORCE_LAYOUT,應(yīng)為到這layout方法已經(jīng)走完了所以得還原以該view前設(shè)置的標(biāo)記位
繼續(xù)看onLayout方法,onLayout要區(qū)分view和viewgroup,view的onLayout是根據(jù)確定的大小做些內(nèi)部邏輯的調(diào)整,viewgroup還得在onLayout中調(diào)用child view 的layout方法,以FrameLayout的onLayout方法為例:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
...
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
...
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
在layoutChildren方法中我們可以看到FrameLayout根據(jù)child view 在measure的時(shí)候確定的測(cè)量大小和gravity確定child view的4個(gè)頂點(diǎn),然后調(diào)用child 的 layout方法開始child view 的layout流程,因?yàn)関iew的實(shí)際大小其實(shí)是由4個(gè)頂點(diǎn)決定的所以說view的實(shí)際大小不一定等于測(cè)量尺寸mMeasureWidth/Height。
layout的起點(diǎn)則是后調(diào)用的ViewRootImpl的performLayout方法
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) {
...
final View host = mView;
if (host == null) {
return;
}
...
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
}
用0,0點(diǎn)DecoerView的測(cè)量寬高作為參數(shù)調(diào)用DecoerView的layout方法開始整個(gè)view 的layout流程。
經(jīng)過上訴2個(gè)過程就確定了每個(gè)view的大小和位置就可以開始繪制流程了,通過ViewRootImpl的perfornDraw進(jìn)入到Draw階段
Draw 階段
從上面的調(diào)用??梢钥吹接猩婕暗?strong>ThreadedRenderer,RenderNode,****RecordingCanvas,****DisplayList 等偏底層類,這幾個(gè)類的大致作用如下:
- ThreadedRenderer是連接java/c++層,ui線程和渲染線程的代理類
- RecordingCanvas記錄繪制過程最后生成DisplayList
- DisplayList就是實(shí)際存放繪制指令的類
- RenderNode則是連接View和對(duì)應(yīng)的DisplayList,每個(gè)View都有自己對(duì)應(yīng)的RenderNode
觸發(fā)了ViewRootImpl的draw后會(huì)調(diào)用到ThreadedRenderer的draw方法,然后調(diào)用updateRootDisplayList把DecorView作為參數(shù)傳遞到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;
}
設(shè)置一些flag然后調(diào)用view的updateDisplayListIfDirty
public RenderNode updateDisplayListIfDirty() {
final RenderNode renderNode = mRenderNode;
...
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| !renderNode.hasDisplayList()
|| (mRecreateDisplayList)) {
if (renderNode.hasDisplayList()
&& !mRecreateDisplayList) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchGetDisplayList();
return renderNode;
}
mRecreateDisplayList = true;
...
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
try {
...
computeScroll();
canvas.translate(-mScrollX, -mScrollY);
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
if (debugDraw()) {
debugDrawFocus(canvas);
}
} else {
draw(canvas);
}
...
} finally {
renderNode.endRecording();
setDisplayListProperties(renderNode);
}
} else {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
return renderNode;
}
- 根據(jù)view的RenderNode是否已經(jīng)持有DisplayList和mRecreateDisplayList(是否需要重建DisplayList標(biāo)記) 判斷是否需要觸發(fā)重建DiplayList
- beginRecording新建用于記錄繪制的RecordingCanvas
- RecordingCanvas作為參數(shù)調(diào)用view.draw(canvas)
- endRecording結(jié)束繪制記錄
draw方法是被beginRecording和endRecording包圍,就有點(diǎn)像由系統(tǒng)beginRecording給view一張紙(RecordingCanvas),view通過draw方法在上面寫如需要在什么位置需要繪制某個(gè)圖形的指令等等,最后endRecording時(shí)后系統(tǒng)回收紙生成對(duì)應(yīng)的DisplayList。
再回頭看下draw方法,和measure方法類似,子view的draw都是由parent view調(diào)用的,parent view需要通過dispatchDraw調(diào)用所有child view的draw方法,
view的draw方法是有兩種
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime){
...
if (hardwareAcceleratedCanvas) {
mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;
mPrivateFlags &= ~PFLAG_INVALIDATED;
}
...
if (drawingWithRenderNode) {
renderNode = updateDisplayListIfDirty();
...
}
...
}
public void draw(Canvas canvas){
...
drawBackground(canvas);
...
onDraw(canvas);
...
dispatchDraw(canvas);
...
}
- 第一個(gè)是由ViewGroup.drawChild()調(diào)用,主要處理和RenderNode等渲染有關(guān)的東西,并調(diào)用updateDisplayListIfDirty方法??梢钥吹皆趗pdateDisplayListIfDirty中觸發(fā)重建displayList的mRecreateDisplayList是由 PFLAG_INVALIDATED決定的,這個(gè)標(biāo)志位正好就是Event 階段調(diào)用requestLayout或者in****validate設(shè)置的
- 第二個(gè)是view在updateDisplayListIfDirty中被調(diào)用的。我們熟知的onDraw也是在這里被調(diào)用的為了防止歧義下文中暫且命名這個(gè)方法為draw_inner
ViewGroup的draw被parent view調(diào)用后根據(jù)mPrivateFlags等決定是否要重建DisplayList,如果需要就會(huì)新建RecordingCanvas作為參數(shù)調(diào)用draw_inner,然后調(diào)用onDraw,平常做自定義控件會(huì)重寫onDraw方法時(shí)調(diào)用canvas.drawXXX等等api,這個(gè)canvas就是RecordingCanvas,它的左右就是記錄繪制的流程,onDraw結(jié)束后會(huì)調(diào)用dispatchDraw遍歷child view調(diào)用每個(gè)view的draw方法,完成所有view的繪制,整個(gè)流程的起點(diǎn)還是DecoverView,在上文中updateViewTreeDisplayList方法中開始的。
當(dāng)所有view 完成繪制了后會(huì)調(diào)用endRecording方法開始生成對(duì)應(yīng)的DisplayList
RenderNode.java
public void endRecording() {
RecordingCanvas canvas = mCurrentRecordingCanvas;
mCurrentRecordingCanvas = null;
long displayList = canvas.finishRecording();
nSetDisplayList(mNativeRenderNode, displayList);
canvas.recycle();
}
RecordingCanvas.java
long finishRecording() {
return nFinishRecording(mNativeCanvasWrapper);
}
結(jié)束繪制返回生成的DisplayList
DisplayList* RecordingCanvas::finishRecording() {
restoreToCount(1);
mPaintMap.clear();
mRegionMap.clear();
mPathMap.clear();
DisplayList* displayList = mDisplayList;
mDisplayList = nullptr;
mSkiaCanvasProxy.reset(nullptr);
return displayList;
}
finishRecording后調(diào)用nSetDisplayList方法關(guān)聯(lián)RenderNode(Java RenderNode 對(duì)應(yīng)的C++類)和對(duì)應(yīng)的DisplayList(C++)
nSetDisplayList方法
static void android_view_RenderNode_setDisplayList(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jlong displayListPtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
DisplayList* newData = reinterpret_cast<DisplayList*>(displayListPtr);
renderNode->setStagingDisplayList(newData);
}
記錄完繪制指令后會(huì)調(diào)用syncAndDrawFrame開始格柵化和交換buffer與SurfaceFlinger通信階段
public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) {
return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length);
}
然后會(huì)依次調(diào)用如下c++類的方法
- android_view_ThreadedRenderer.cppandroid_view_ThreadedRenderer_syncAndDrawFrame()
- RenderProxy.cppRenderProxy::syncAndDrawFrame()
- DrawFrameTask.cppDrawFrameTask::run()
- CanvasContext.cpp
void CanvasContext::draw()
{
...
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
mContentDrawBound, mOpaque, mLightInfo, mRenderNodes, &(profiler()));
...
bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo,
&requireSwap);
...
}
mRenderPipeline的draw和swapBuffers方法就是對(duì)應(yīng)的格柵化和交換 buffer了。到此 Android 原生端的繪制流程就算結(jié)束了。
Flutter
FrameWork構(gòu)架
Flutter擁有自己的開發(fā)工具,開發(fā)語言、虛擬機(jī),編譯機(jī)制,線程模型和渲染管線,和Android相比,它也可以看做一個(gè)小型的OS了。
先看下flutter framework的構(gòu)架圖
從下到上依次:
Embedder
不同的操作系統(tǒng)有不同的embedder,它負(fù)責(zé)為fluuter提供運(yùn)行的入口,提供像rendering surface,輸入系統(tǒng),管理message event loop等服務(wù),甚至可以根據(jù)embedder api移植到不同的系統(tǒng)上,如非官方支持desktop的https://github.com/go-flutter-desktop/go-flutter
Flutter engine
作為flutter app的基石,提供dart runtime ,文字圖像繪制,柵格化,文件,網(wǎng)絡(luò)等功能,通過dart:ui像framework層提供一些底層的api
Framework
我們開發(fā)過程中接觸最多的就是framework層,給我們提供了不同風(fēng)格的控件,動(dòng)畫。手勢(shì),繪制等支持,管理我們配置的widget tree,處理一部分渲染流程,本文主要也是在這層展開。
未完待續(xù)。。。
參考:
一顆像素的誕生:
https://mp.weixin.qq.com/s/QoFrdmxdRJG5ETQp5Ua3-A
Flutter architectural-overview:
https://flutter.cn/docs/resources/architectural-overview#widgets
Choreographer原理
http://gityuan.com/2017/02/25/choreographer/
Android N中UI硬件渲染(hwui)的HWUI_NEW_OPS(基于Android 7.1)