一顾孽、SurfaceView和View的繪制流程
1.View的繪制流程
View的繪制流程可以分為三個(gè)階段:measure(測(cè)量)末秃、layout(布局)和draw(繪制)笋妥。這三個(gè)階段是從ViewRootImpl的performTraversals()方法開始,自上而下地遍歷整個(gè)View樹,對(duì)每個(gè)View進(jìn)行相應(yīng)的操作秉颗。
① measure
階段的目的是確定每個(gè)View的寬度和高度。
這個(gè)階段會(huì)調(diào)用每個(gè)View的measure()方法送矩,該方法會(huì)根據(jù)View的LayoutParams和父View的MeasureSpecs來(lái)計(jì)算出View的MeasureSpecs蚕甥,然后傳遞給View的onMeasure()方法,該方法會(huì)根據(jù)View的MeasureSpecs和自身的內(nèi)容來(lái)設(shè)置View的MeasuredWidth和MeasuredHeight栋荸。View的MeasureSpecs是一個(gè)32位的整數(shù)菇怀,其中高2位表示測(cè)量模式(MeasureSpec.EXACTLY、MeasureSpec.AT_MOST或MeasureSpec.UNSPECIFIED)蒸其,低30位表示測(cè)量大小。View的MeasuredWidth和MeasuredHeight是一個(gè)16位的整數(shù)库快,表示View在測(cè)量階段的寬度和高度摸袁。
②layout
階段的目的是確定每個(gè)View的位置。
這個(gè)階段會(huì)調(diào)用每個(gè)View的layout()方法义屏,該方法會(huì)根據(jù)View的MeasuredWidth和MeasuredHeight以及父View的位置和邊距來(lái)設(shè)置View的Left靠汁、Top、Right和Bottom闽铐。這四個(gè)屬性表示View在父View坐標(biāo)系中的位置和大小蝶怔。View的layout()方法會(huì)調(diào)用View的onLayout()方法,該方法會(huì)根據(jù)View的子View的MeasuredWidth和MeasuredHeight以及View自身的布局規(guī)則來(lái)設(shè)置子View的位置兄墅。View的onLayout()方法是一個(gè)抽象方法踢星,需要由具體的子類來(lái)實(shí)現(xiàn),例如LinearLayout隙咸、RelativeLayout等沐悦。
③draw
階段的目的是將每個(gè)View的內(nèi)容繪制到屏幕上。
這個(gè)階段會(huì)調(diào)用每個(gè)View的draw()方法五督,該方法會(huì)創(chuàng)建一個(gè)Canvas對(duì)象藏否,該對(duì)象封裝了一個(gè)Bitmap對(duì)象,該Bitmap對(duì)象表示View的繪制緩沖區(qū)充包。View的draw()方法會(huì)調(diào)用View的onDraw()方法副签,該方法會(huì)使用Canvas對(duì)象的繪圖方法來(lái)繪制View的內(nèi)容,例如drawText()基矮、drawBitmap()等淆储。View的draw()方法還會(huì)調(diào)用View的dispatchDraw()方法,該方法會(huì)遍歷View的子View家浇,并調(diào)用子View的draw()方法遏考,從而實(shí)現(xiàn)View樹的遞歸繪制。View的繪制緩沖區(qū)最終會(huì)被合成到屏幕上蓝谨,這個(gè)過(guò)程由硬件加速或軟件渲染來(lái)完成灌具。
下面是View的繪制流程的源碼分析青团,以ViewRootImpl的performTraversals()方法為入口:
void performTraversals() {
// ..........
boolean viewScrolled = false;
// 第一次繪制或者窗口大小發(fā)生變化時(shí),執(zhí)行measure階段
if (mFirst || mReportNextDraw) {
mFullRedrawNeeded = true;
mLayoutRequested = true;
}
boolean windowSizeMayChange = false;
// 獲取窗口的寬度和高度
int desiredWindowWidth = mWinFrame.width();
int desiredWindowHeight = mWinFrame.height();
// ..........
// 如果需要執(zhí)行measure階段
if (mLayoutRequested && !mStopped) {
// ..........
// 調(diào)用View的measure()方法咖楣,傳入窗口的寬度和高度作為MeasureSpecs
performMeasure(desiredWindowWidth, desiredWindowHeight);
// 獲取View的MeasuredWidth和MeasuredHeight
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
// ..........
}
// ..........
// 如果需要執(zhí)行l(wèi)ayout階段
if ((mLayoutRequested || windowSizeMayChange) && !mStopped) {
// ..........
// 調(diào)用View的layout()方法督笆,傳入View的Left、Top诱贿、Right和Bottom
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
// ..........
}
// ..........
// 如果需要執(zhí)行draw階段
if (!mStopped) {
// ..........
// 調(diào)用View的draw()方法娃肿,傳入一個(gè)Canvas對(duì)象
performDraw(canvas);
// ..........
}
// ..........
}
2.SurfaceView的繪制流程
SurfaceView的繪制流程可以分為兩個(gè)階段:create(創(chuàng)建)和draw(繪制)。這兩個(gè)階段是通過(guò)SurfaceHolder的回調(diào)方法來(lái)觸發(fā)的珠十,create階段的目的是創(chuàng)建一個(gè)Surface料扰,draw階段的目的是在Surface上繪制內(nèi)容。
① create
階段
調(diào)用SurfaceHolder.Callback的surfaceCreated()和surfaceChanged()方法焙蹭,這兩個(gè)方法會(huì)在Surface被創(chuàng)建或者改變時(shí)被調(diào)用晒杈。在這個(gè)階段,可以獲取SurfaceHolder對(duì)象孔厉,該對(duì)象封裝了一個(gè)Surface對(duì)象拯钻,該Surface對(duì)象表示SurfaceView的繪制緩沖區(qū)∽颍可以在這個(gè)階段創(chuàng)建一個(gè)繪制線程粪般,并將SurfaceHolder對(duì)象傳遞給該線程,以便在后臺(tái)進(jìn)行繪制操作污桦。
① draw
階段
調(diào)用SurfaceHolder.Callback的surfaceDestroyed()方法亩歹,該方法會(huì)在Surface被銷毀時(shí)被調(diào)用。在這個(gè)階段凡橱,可以停止繪制線程捆憎,并釋放SurfaceHolder對(duì)象。繪制線程可以在運(yùn)行時(shí)獲取SurfaceHolder對(duì)象的鎖梭纹,然后獲取一個(gè)Canvas對(duì)象躲惰,該對(duì)象封裝了Surface對(duì)象,然后使用Canvas對(duì)象的繪圖方法來(lái)繪制內(nèi)容变抽,例如drawText()础拨、drawBitmap()等。繪制完成后绍载,需要釋放Canvas
下面是SurfaceView的繪制流程的源碼分析诡宗,以SurfaceView的init()方法為入口:
private void init() {
// ..........
// 創(chuàng)建一個(gè)SurfaceHolder對(duì)象,該對(duì)象封裝了一個(gè)Surface對(duì)象
mSurfaceHolder = new SurfaceHolder() {
// ..........
// 獲取Surface對(duì)象的鎖
@Override
public Canvas lockCanvas() {
return internalLockCanvas(null, false);
}
// ..........
// 釋放Surface對(duì)象的鎖击儡,并將繪制內(nèi)容合成到屏幕上
@Override
public void unlockCanvasAndPost(Canvas canvas) {
mSurface.unlockCanvasAndPost(canvas);
// ..........
}
// ..........
// 添加SurfaceHolder.Callback對(duì)象塔沃,用于監(jiān)聽Surface的狀態(tài)變化
@Override
public void addCallback(Callback callback) {
synchronized (mCallbacks) {
// This is a linear search, but in practice we'll
// have only a couple callbacks, so it doesn't matter.
if (mCallbacks.contains(callback) == false) {
mCallbacks.add(callback);
}
}
}
// ..........
};
// ..........
}
二、SurfaceView和View的繪制原理
1.View的繪制原理
View的繪制是基于Window的Surface來(lái)實(shí)現(xiàn)的阳谍,Window的Surface是一個(gè)原生的緩沖區(qū)蛀柴,用來(lái)保存當(dāng)前窗口的像素?cái)?shù)據(jù)螃概,它是由屏幕顯示內(nèi)容合成器(Screen Compositor)所管理的。屏幕顯示內(nèi)容合成器是一個(gè)系統(tǒng)級(jí)的服務(wù)鸽疾,它負(fù)責(zé)將所有Window的Surface合成到屏幕上吊洼,形成最終的顯示效果。屏幕顯示內(nèi)容合成器的具體實(shí)現(xiàn)可能有所不同制肮,例如SurfaceFlinger冒窍、HWComposer等,但它們的基本原理都是類似的豺鼻。
View的繪制過(guò)程中综液,會(huì)通過(guò)Window的lockCanvas方法,鎖定Window的Surface中的Canvas對(duì)象儒飒,用來(lái)在Surface上繪制View的內(nèi)容谬莹,繪制完成后,會(huì)通過(guò)unlockCanvasAndPost方法约素,解鎖Surface中的Canvas對(duì)象届良,并將繪制結(jié)果提交到Surface的緩沖區(qū)中笆凌。Surface會(huì)將緩沖區(qū)中的內(nèi)容交給屏幕顯示內(nèi)容合成器圣猎,由它來(lái)將Surface的內(nèi)容合成到屏幕上,完成繪制流程乞而。
下面我們重點(diǎn)分析draw階段的源碼送悔,draw階段的源碼如下:
private void performDraw(Canvas canvas) {
// .............
// 如果沒(méi)有傳入Canvas對(duì)象,就從Window的Surface中
// 獲取一個(gè)Canvas對(duì)象爪模,用于在Surface上繪制View的內(nèi)容
if (canvas == null) {
// If the view hierarchy contains a SurfaceView that is not
// updated (asynchronous mode), we will use the previous frame
// to render the view hierarchy. This means we won't see the
// updated state of the SurfaceView but there is no way around
// this, unless we were to wait for the new frame to be ready.
// But then, we would end up with a blank frame and drop below
// 60 fps.
if (mAttachInfo.mThreadedRenderer != null) {
canvas = mAttachInfo.mThreadedRenderer.getCanvas();
} else {
canvas = mSurface.lockCanvas(mDirty);
}
}
// .............
// 調(diào)用View的draw()方法欠啤,傳入Canvas對(duì)象,用于在Surface上繪制View的內(nèi)容屋灌,
// 該方法會(huì)先繪制View的背景洁段,然后根據(jù)是否需要邊緣漸變效果,
// 調(diào)用onDraw方法或者drawWithFadingEdges方法共郭,用于在Surface上繪制View的內(nèi)容祠丝,
// 然后調(diào)用dispatchDraw方法,用于在Surface上繪制View的子View的內(nèi)容除嘹,
// 然后調(diào)用onDrawForeground方法写半,用于在Surface上繪制View的前景,
// 最后調(diào)用debugDrawFocus方法尉咕,用于在Surface上繪制View的焦點(diǎn)狀態(tài)
mAttachInfo.mView.draw(canvas);
// .............
// 如果從Window的Surface中獲取了Canvas對(duì)象叠蝇,
// 就釋放Surface中的Canvas對(duì)象,
// 并將繪制結(jié)果提交到Surface的緩沖區(qū)中
if (mAttachInfo.mThreadedRenderer == null) {
mSurface.unlockCanvasAndPost(canvas);
}
// .............
}
如果沒(méi)有傳入Canvas對(duì)象年缎,就從Window的Surface中獲取一個(gè)Canvas對(duì)象悔捶,用于在Surface上繪制View的內(nèi)容铃慷,該過(guò)程是通過(guò)Surface的lockCanvas方法實(shí)現(xiàn)的,該方法會(huì)返回一個(gè)Canvas對(duì)象炎功,用于在Surface上繪制內(nèi)容枚冗。該方法的源碼如下:
public Canvas lockCanvas(Rect inOutDirty) throws Surface.OutOfResourcesException,
IllegalArgumentException {
synchronized (mLock) {
checkNotReleasedLocked();
if (mLockedObject != 0) {
throw new IllegalArgumentException("Surface was already locked");
}
mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
return mCanvas;
}
}
lockCanvas
方法主要做了以下幾件事:
同步鎖定mLock對(duì)象,用于保證線程安全蛇损。
檢查Surface是否已經(jīng)被釋放赁温,如果是,拋出異常淤齐。
檢查Surface是否已經(jīng)被鎖定股囊,如果是,拋出異常更啄。
調(diào)用nativeLockCanvas方法稚疹,傳入mNativeObject、mCanvas和inOutDirty參數(shù)祭务,該方法會(huì)返回一個(gè)mLockedObject對(duì)象内狗,用于標(biāo)識(shí)Surface的鎖定狀態(tài)。
返回mCanvas對(duì)象义锥,用于在Surface上繪制內(nèi)容柳沙。
調(diào)用View的draw()方法,傳入Canvas對(duì)象拌倍,用于在Surface上繪制View的內(nèi)容赂鲤,draw()方法會(huì)先繪制View的背景,然后根據(jù)是否需要邊緣漸變效果柱恤,調(diào)用onDraw方法或者drawWithFadingEdges方法数初,用于在Surface上繪制View的內(nèi)容,然后調(diào)用dispatchDraw方法梗顺,用于在Surface上繪制View的子View的內(nèi)容泡孩,然后調(diào)用onDrawForeground方法,用于在Surface上繪制View的前景寺谤,最后調(diào)用debugDrawFocus方法仑鸥,用于在Surface上繪制View的焦點(diǎn)狀態(tài)。該方法的源碼如下:
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;
if (!dirtyOpaque) {
drawBackground(canvas);
}
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// common case onDraw(canvas);
} else {
// …
}
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
onDrawForeground(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
}
draw()
方法主要做了以下幾件事:
- 清除View的臟標(biāo)志矗漾,設(shè)置View的繪制標(biāo)志锈候,表示View已經(jīng)被繪制過(guò)。
- 如果View不是完全不透明的敞贡,繪制View的背景泵琳,該方法會(huì)調(diào)用Drawable的draw方法,傳入Canvas對(duì)象,用于在Surface上繪制Drawable的內(nèi)容获列。
- 根據(jù)View的邊緣漸變標(biāo)志谷市,判斷是否需要繪制邊緣漸變效果,如果不需要击孩,直接調(diào)用onDraw方法迫悠,傳入Canvas對(duì)象,用于在Surface上繪制View的內(nèi)容巩梢,該方法是一個(gè)空方法创泄,一般由子類重寫,實(shí)現(xiàn)具體的繪制邏輯括蝠,例如TextView會(huì)繪制文本鞠抑,ImageView會(huì)繪制圖片等。如果需要忌警,調(diào)用drawWithFadingEdges方法搁拙,傳入Canvas對(duì)象,用于在Surface上繪制帶有邊緣漸變效果的View的內(nèi)容法绵,該方法會(huì)先保存Canvas的狀態(tài)箕速,然后根據(jù)邊緣漸變的方向,裁剪Canvas的區(qū)域朋譬,然后調(diào)用onDraw方法盐茎,傳入Canvas對(duì)象,用于在Surface上繪制View的內(nèi)容此熬,最后恢復(fù)Canvas的狀態(tài)庭呜。
- 調(diào)用dispatchDraw方法滑进,傳入Canvas對(duì)象犀忱,用于在Surface上繪制View的子View的內(nèi)容,該方法會(huì)遍歷View的子View列表扶关,根據(jù)它們的可見性阴汇、透明度、動(dòng)畫等屬性节槐,決定是否需要繪制搀庶,如果需要,就調(diào)用子View的draw方法铜异,傳入Canvas對(duì)象哥倔,用于在Surface上繪制子View的內(nèi)容,該方法是一個(gè)遞歸的過(guò)程揍庄,直到所有的子View都被繪制完畢咆蒿。
- 如果View有Overlay,且Overlay不為空,調(diào)用Overlay的getOverlayView方法沃测,獲取Overlay的View對(duì)象缭黔,然后調(diào)用Overlay的View對(duì)象的dispatchDraw方法,傳入Canvas對(duì)象蒂破,用于在Surface上繪制Overlay的內(nèi)容馏谨,該方法與View的dispatchDraw方法類似,也是一個(gè)遞歸的過(guò)程附迷,直到所有的Overlay的子View都被繪制完畢惧互。
- 調(diào)用onDrawForeground方法,傳入Canvas對(duì)象喇伯,用于在Surface上繪制View的前景壹哺,該方法會(huì)繪制View的滾動(dòng)條、前景Drawable等內(nèi)容艘刚。
- 如果開啟了調(diào)試模式管宵,調(diào)用debugDrawFocus方法,傳入Canvas對(duì)象攀甚,用于在Surface上繪制View的焦點(diǎn)狀態(tài)巫糙,該方法會(huì)繪制View的邊框、焦點(diǎn)框等內(nèi)容哼勇。
如果從Window的Surface中獲取了Canvas對(duì)象惯疙,就釋放Surface中的Canvas對(duì)象,并將繪制結(jié)果提交到Surface的緩沖區(qū)中荚斯,該過(guò)程是通過(guò)Surface的unlockCanvasAndPost方法實(shí)現(xiàn)的埠居,該方法會(huì)釋放Surface中的Canvas對(duì)象,并將繪制結(jié)果提交到Surface的緩沖區(qū)中事期。unlockCanvasAndPost方法的源碼如下:
public void unlockCanvasAndPost(Canvas canvas) {
synchronized (mLock) {
checkNotReleasedLocked();
if (mLockedObject == 0) {
throw new IllegalStateException("Surface was not locked");
}
nativeUnlockCanvasAndPost(mNativeObject, canvas);
nativeRelease(mLockedObject);
mLockedObject = 0;
}
}
unlockCanvasAndPost方法主要做了以下幾件事:
同步鎖定mLock對(duì)象滥壕,用于保證線程安全。
檢查Surface是否已經(jīng)被釋放兽泣,如果是绎橘,拋出異常。
檢查Surface是否已經(jīng)被鎖定唠倦,如果否称鳞,拋出異常。
調(diào)用nativeUnlockCanvasAndPost方法稠鼻,傳入mNativeObject和canvas參數(shù)冈止,該方法會(huì)將繪制結(jié)果提交到Surface的緩沖區(qū)中,并通知SurfaceFlinger進(jìn)行合成候齿。
調(diào)用nativeRelease方法熙暴,傳入mLockedObject參數(shù)苫亦,該方法會(huì)釋放Surface的鎖定狀態(tài),并減少Surface的強(qiáng)引用計(jì)數(shù)怨咪。
Surface會(huì)將緩沖區(qū)中的內(nèi)容交給屏幕顯示內(nèi)容合成器屋剑,由它來(lái)將Surface的內(nèi)容合成到屏幕上,完成繪制流程诗眨。屏幕顯示內(nèi)容合成器是一個(gè)系統(tǒng)級(jí)的服務(wù)唉匾,它負(fù)責(zé)將所有Window的Surface和其他層合成到屏幕上,形成最終的顯示效果匠楚。屏幕顯示內(nèi)容合成器的源碼位于frameworks/native/services/surfaceflinger目錄下巍膘,它是一個(gè)C++的程序,使用OpenGL ES來(lái)進(jìn)行圖形渲染芋簿。屏幕顯示內(nèi)容合成器的主要類和方法如下:
SurfaceFlinger類峡懈,它是屏幕顯示內(nèi)容合成器的核心類,它管理著所有的Surface和Layer与斤,以及與客戶端的通信和與硬件的交互肪康。
SurfaceFlinger::onMessageReceived方法,它是SurfaceFlinger的消息處理方法撩穿,它會(huì)根據(jù)不同的消息類型磷支,執(zhí)行不同的操作,例如創(chuàng)建Surface食寡、銷毀Surface雾狈、更新Surface、合成Surface等抵皱。
SurfaceFlinger::doComposition方法善榛,它是SurfaceFlinger的合成方法,它會(huì)遍歷所有的Layer呻畸,根據(jù)它們的Z-order(層級(jí)順序)移盆,將它們的內(nèi)容繪制到一個(gè)FrameBuffer對(duì)象上,然后將FrameBuffer對(duì)象的內(nèi)容顯示到屏幕上擂错。
2.SurfaceView的繪制原理
SurfaceView的繪制原理是基于SurfaceView自己的Surface來(lái)實(shí)現(xiàn)的味滞,SurfaceView的Surface是一個(gè)獨(dú)立于Window的Surface樱蛤,用來(lái)展示Surface中的數(shù)據(jù)钮呀。
以SurfaceView的init()
方法為入口,init()方法是SurfaceView的初始化方法昨凡,它會(huì)創(chuàng)建一個(gè)SurfaceHolder對(duì)象爽醋,用于管理SurfaceView的Surface,以及一個(gè)SurfaceViewUpdateThread對(duì)象便脊,用于在非UI線程中更新SurfaceView的內(nèi)容蚂四。
init()
方法的源碼如下:
private void init() {
// .............
// 創(chuàng)建一個(gè)SurfaceHolder對(duì)象,用于管理SurfaceView的Surface
mSurfaceHolder = new SurfaceHolder() {
// .............
};
// 創(chuàng)建一個(gè)SurfaceViewUpdateThread對(duì)象,用于在非UI線程中更新SurfaceView的內(nèi)容
mSurfaceViewUpdateThread = new SurfaceViewUpdateThread();
// .............
}
init()
方法主要做了以下幾件事:
- 創(chuàng)建一個(gè)SurfaceHolder對(duì)象遂赠,用于管理SurfaceView的Surface久妆,該對(duì)象提供了一些方法,用于獲取跷睦、鎖定筷弦、解鎖和銷毀Surface,以及設(shè)置Surface的格式抑诸、尺寸烂琴、回調(diào)等屬性。
- 創(chuàng)建一個(gè)SurfaceViewUpdateThread對(duì)象蜕乡,用于在非UI線程中更新SurfaceView的內(nèi)容奸绷,該對(duì)象是一個(gè)繼承自Thread的子類,它重寫了run()方法层玲,用于在循環(huán)中不斷地繪制SurfaceView的內(nèi)容号醉。
下面重點(diǎn)分析SurfaceView的繪制過(guò)程,通過(guò)SurfaceHolder的lockCanvas方法辛块,鎖定SurfaceView的Surface中的Canvas對(duì)象扣癣,用來(lái)在Surface上繪制內(nèi)容,繪制完成后憨降,會(huì)通過(guò)unlockCanvasAndPost方法父虑,解鎖Surface中的Canvas對(duì)象,并將繪制結(jié)果提交到Surface的緩沖區(qū)中授药。SurfaceFlinger會(huì)將SurfaceView的Surface和其他層合成到屏幕上士嚎,完成繪制流程。
繪制過(guò)程的源碼如下:
class SurfaceViewUpdateThread extends Thread {
// .............
@Override
public void run() {
// .............
// 循環(huán)繪制SurfaceView的內(nèi)容
while (mRunning) {
// .............
// 通過(guò)SurfaceHolder的lockCanvas方法悔叽,鎖定SurfaceView的Surface中的Canvas對(duì)象莱衩,用來(lái)在Surface上繪制內(nèi)容
Canvas canvas = mSurfaceHolder.lockCanvas();
if (canvas != null) {
// .............
// 在Canvas上繪制SurfaceView的內(nèi)容,例如娇澎,使用Paint對(duì)象繪制顏色笨蚁、文本、圖形等
canvas.drawColor(Color.BLACK);
canvas.drawText("Hello, SurfaceView!", 100, 100, mPaint);
// .............
// 通過(guò)SurfaceHolder的unlockCanvasAndPost方法趟庄,解鎖Surface中的Canvas對(duì)象括细,并將繪制結(jié)果提交到Surface的緩沖區(qū)中
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
// .............
}
}
}
繪制過(guò)程主要做了以下兩件事:
循環(huán)繪制SurfaceView的內(nèi)容,直到線程停止或者Surface被銷毀戚啥。
通過(guò)SurfaceHolder的lockCanvas方法奋单,鎖定SurfaceView的Surface中的Canvas對(duì)象,用來(lái)在Surface上繪制內(nèi)容猫十,lockCanvas方法會(huì)返回一個(gè)Canvas對(duì)象览濒,用于在Surface上繪制內(nèi)容呆盖。
lockCanvas
方法的源碼如下:
public Canvas lockCanvas() {
return internalLockCanvas(null, false);
}
private final Canvas internalLockCanvas(Rect dirty, boolean hardware) {
// .............
// 調(diào)用Surface的lockCanvas方法,傳入dirty參數(shù)贷笛,該方法會(huì)返回一個(gè)Canvas對(duì)象应又,用于在Surface上繪制內(nèi)容
Canvas c = mSurface.lockCanvas(dirty);
// .............
return c;
}
lockCanvas
方法主要做了以下兩件事:
調(diào)用Surface的lockCanvas方法,傳入dirty參數(shù)乏苦,該方法會(huì)返回一個(gè)Canvas對(duì)象丁频,用于在Surface上繪制內(nèi)容,該方法的源碼與前面分析的Window的Surface的lockCanvas方法相同邑贴,不再贅述席里。
在Canvas上繪制SurfaceView的內(nèi)容,例如拢驾,使用Paint對(duì)象繪制顏色奖磁、文本、圖形等繁疤,該過(guò)程與普通的View的繪制過(guò)程類似咖为,不再贅述。
通過(guò)SurfaceHolder的unlockCanvasAndPost方法稠腊,解鎖Surface中的Canvas對(duì)象躁染,并將繪制結(jié)果提交到Surface的緩沖區(qū)中,unlockCanvasAndPost方法會(huì)釋放Surface中的Canvas對(duì)象架忌,并將繪制結(jié)果提交到Surface的緩沖區(qū)中吞彤。
unlockCanvasAndPost
方法的源碼如下:
public void unlockCanvasAndPost(Canvas canvas) {
// .............
// 調(diào)用Surface的unlockCanvasAndPost方法,傳入canvas參數(shù)叹放,該方法會(huì)釋放Surface中的Canvas對(duì)象饰恕,并將繪制結(jié)果提交到Surface的緩沖區(qū)中
mSurface.unlockCanvasAndPost(canvas);
// .............
}
unlockCanvasAndPost
方法主要做了以下兩件事:
調(diào)用Surface的unlockCanvasAndPost方法,傳入canvas參數(shù)井仰,該方法會(huì)釋放Surface中的Canvas對(duì)象埋嵌,并將繪制結(jié)果提交到Surface的緩沖區(qū)中,該方法的源碼與前面分析的Window的Surface的unlockCanvasAndPost方法相同俱恶,不再贅述雹嗦。
SurfaceFlinger會(huì)將SurfaceView的Surface和其他層合成到屏幕上,完成繪制流程合是,該過(guò)程與前面分析的Window的Surface的合成過(guò)程類似了罪,不再贅述。
三端仰、SurfaceView和View內(nèi)的Canvas的區(qū)別
View使用的Canvas | SurfaceView使用的Canvas | |
---|---|---|
所屬的Surface | Window的Surface | SurfaceView自己的Surface |
繪制線程 | 只能在UI線程中繪制 | 可以在獨(dú)立的線程中繪制 |
繪制時(shí)機(jī) | 受到View樹的測(cè)量捶惜、布局和繪制的影響,需要等待Window的刷新 | 不受View的繪制流程的控制荔烧,可以在任何時(shí)候進(jìn)行繪制 |