之前我們講了Surface的創(chuàng)建過(guò)程和SurfaceView的"挖洞"過(guò)程
)。這一篇我們就開始講講SurfaceView的完整繪制流程
前面也有說(shuō)過(guò),雖然SurfaceView具有自己的獨(dú)立的surface黔寇,但是畢竟是在一個(gè)View Hierarchy中索守,所以依然還會(huì)遵循View的繪制流程访锻。當(dāng)ViewRootImpl在執(zhí)行performTravesals的時(shí)候,回一次執(zhí)行performMeasure玖绿,performLayout和performDraw。如何測(cè)量和布局不是本文的重點(diǎn)叁巨,我們直接關(guān)注performDraw好了
private void performDraw() {
...代碼省略...
try {
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
}
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
...代碼省略...
}
private boolean draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (!surface.isValid()) {
return false;
}
...代碼省略...
boolean useAsyncReport = false;
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
...代碼省略...
} else {
...代碼省略...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
return useAsyncReport;
}
performDraw 里面調(diào)用的是draw的方法斑匪,draw方法里面先賦值了surface,然后調(diào)用drawSoftware方法锋勺,將surface傳入:
* @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, Rect surfaceInsets) {
...代碼省略...
// Draw with software renderer.
final Canvas canvas;
try {
...代碼省略...
canvas = mSurface.lockCanvas(dirty);
...代碼省略...
}
...代碼省略...
try {
...代碼省略...
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;
}
對(duì)這段代碼是否比較眼熟?沒(méi)錯(cuò)蚀瘸,SurfaceView平時(shí)在繪制的時(shí)候就會(huì)讓我們?cè)谧泳€程中先通過(guò)Surface獲取canvas,然后canvas操作完以后需要通過(guò)surface的unlockCanvasAndPost進(jìn)行屏幕刷新庶橱。這里面普通的View也是一樣的操作贮勃,只不過(guò)將surface封裝了起來(lái),對(duì)于開發(fā)者來(lái)說(shuō)是不透明的而已苏章。當(dāng)然這些也不是本文的重點(diǎn)衙猪。我們繼續(xù)往下看,這里最終會(huì)調(diào)用mView的draw方法布近,由于DecorView 和ViewGroup都沒(méi)有重寫draw方法垫释,所以直接進(jìn)入View的draw方法中
public void draw(Canvas 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);
drawAutofilledHighlight(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);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
...代碼省略...
}
這里面就是View draw的主要操作,由于當(dāng)前仍然是DecorView撑瞧,所以在執(zhí)行dispatchDraw 的時(shí)候會(huì)調(diào)用到ViewGroup的dispatchDraw方法,而dispatchDraw方法又調(diào)用drawChild棵譬,最終還是調(diào)用會(huì)View的draw方法,不過(guò)這次的draw方法跟剛才的略有不同:
/**
* This method is called by ViewGroup.drawChild() to have each child view draw itself.
*
* This is where the View specializes rendering behavior based on layer type,
* and hardware acceleration.
*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
...代碼省略...
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) {
...代碼省略...
}
...代碼省略...
return more;
}
這里面判斷(mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW條件成立的時(shí)候會(huì)調(diào)用dispatchDraw方法预伺,條件不成立的時(shí)候才會(huì)draw订咸。之前我們有講過(guò)SurfaceView在初始化的時(shí)候就會(huì)調(diào)用setWillNotDraw(true)方法:
void setflags(int flags, int mask){
if ((changed & DRAW_MASK) != 0) {
if ((mViewFlags & WILL_NOT_DRAW) != 0) {
if (mBackground != null
|| mDefaultFocusHighlight != null
|| (mForegroundInfo != null && mForegroundInfo.mDrawable != null)) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
} else {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
} else {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
}
requestLayout();
invalidate(true);
}
}
從這里可以看出來(lái)我們?nèi)绻麤](méi)有對(duì)View設(shè)置背景或者前景,然后當(dāng)我們?cè)O(shè)置了setWillNotDraw(true)之后酬诀,此時(shí)mPrivateFlags會(huì)和PFLAG_SKIP_DRAW做一次邏輯或脏嚷。所以SurfaceView最終是不會(huì)調(diào)用draw方法的,而調(diào)用了dispatchDraw方法瞒御,那我們?cè)谶M(jìn)入SurfaceView的dispatchDraw方法看看:
@Override
protected void dispatchDraw(Canvas canvas) {
if (mDrawFinished && !isAboveParent()) {
// draw() is not called when SKIP_DRAW is set
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
// punch a whole in the view-hierarchy below us
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
}
super.dispatchDraw(canvas);
}
最終SurfaceView只是簡(jiǎn)單地將它所占據(jù)的區(qū)域繪制為黑色父叙,所以我們看到初始化的SurfaceView會(huì)是一個(gè)黑色的View。
所以SurfaceView無(wú)法在onDraw方法上面進(jìn)行繪制,那么我們就只能自己主動(dòng)的進(jìn)行繪制了趾唱。又因?yàn)镾urfaceView不需要告知ViewRootImpl進(jìn)行CheckThread操作涌乳,所以可以直接放在子線程中進(jìn)行操作:
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
SurfaceHolder holder = mSurfaceView.getHolder();
Canvas canvas = holder.lockCanvas();
//canvas 的繪制操作
holder.unlockCanvasAndPost(canvas);
}
}
}).start();
這里我們細(xì)細(xì)講下這三步主要做了什么。
先來(lái)看第一步:surfaceView.getHolder()
/**
* Return the SurfaceHolder providing access and control over this
* SurfaceView's underlying surface.
*
* @return SurfaceHolder The holder of the surface.
*/
public SurfaceHolder getHolder() {
return mSurfaceHolder;
}
直接返回mSurfaceHolder對(duì)象甜癞,我們看下mSurfaceHolder是什么:
private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
...代碼省略...
};
其實(shí)就是在SurfaceView初始化的時(shí)候就會(huì)初始化SurfaceHolder夕晓。
那么接下來(lái)看第二步:holder.lockCanvas();
SurfaceHolder本身是一個(gè)接口,但是它在SurfaceView里面初始化的時(shí)候?qū)崿F(xiàn)了里面的方法:
private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
@Override
public Canvas lockCanvas() {
return internalLockCanvas(null, false);
}
private Canvas internalLockCanvas(Rect dirty, boolean hardware) {
mSurfaceLock.lock();
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
+ mDrawingStopped + ", surfaceControl=" + mSurfaceControl);
Canvas c = null;
if (!mDrawingStopped && mSurfaceControl != null) {
try {
if (hardware) {
c = mSurface.lockHardwareCanvas();
} else {
c = mSurface.lockCanvas(dirty);
}
} catch (Exception e) {
Log.e(LOG_TAG, "Exception locking surface", e);
}
}
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c);
if (c != null) {
mLastLockTime = SystemClock.uptimeMillis();
return c;
}
// If the Surface is not ready to be drawn, then return null,
// but throttle calls to this function so it isn't called more
// than every 100ms.
long now = SystemClock.uptimeMillis();
long nextTime = mLastLockTime + 100;
if (nextTime > now) {
try {
Thread.sleep(nextTime-now);
} catch (InterruptedException e) {
}
now = SystemClock.uptimeMillis();
}
mLastLockTime = now;
mSurfaceLock.unlock();
return null;
}
};
lockCanvas會(huì)調(diào)用internalLockCanvas方法悠咱,傳入的參數(shù)是null和false蒸辆,前者傳null就表示要獲取整塊的canvas,后者傳false是代表不要硬件加速析既。最后會(huì)調(diào)用Surface的lockCanvas方法:
public Canvas lockCanvas(Rect inOutDirty)
throws Surface.OutOfResourcesException, IllegalArgumentException {
synchronized (mLock) {
checkNotReleasedLocked();
if (mLockedObject != 0) {
// Ideally, nativeLockCanvas() would throw in this situation and prevent the
// double-lock, but that won't happen if mNativeObject was updated. We can't
// abandon the old mLockedObject because it might still be in use, so instead
// we just refuse to re-lock the Surface.
throw new IllegalArgumentException("Surface was already locked");
}
mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
return mCanvas;
}
}
會(huì)發(fā)現(xiàn)這個(gè)時(shí)候需要通過(guò)C++層將canvas傳回來(lái)吁朦。
/frameworks/base/core/jni/android_view_Surface.cpp
static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
if (!isSurfaceValid(surface)) {
doThrowIAE(env);
return 0;
}
Rect dirtyRect(Rect::EMPTY_RECT);
Rect* dirtyRectPtr = NULL;
if (dirtyRectObj) {
//獲取臟數(shù)據(jù)區(qū)域,也就是要更新的范圍渡贾,這里傳了null逗宜,所以dirtyRect不設(shè)置區(qū)域默認(rèn)整個(gè)View都要更新
dirtyRect.left = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
dirtyRect.top = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
dirtyRect.right = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
dirtyRectPtr = &dirtyRect;
}
ANativeWindow_Buffer outBuffer;
status_t err = surface->lock(&outBuffer, dirtyRectPtr);
if (err < 0) {
const char* const exception = (err == NO_MEMORY) ?
OutOfResourcesException :
"java/lang/IllegalArgumentException";
jniThrowException(env, exception, NULL);
return 0;
}
SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
convertPixelFormat(outBuffer.format),
outBuffer.format == PIXEL_FORMAT_RGBX_8888
? kOpaque_SkAlphaType : kPremul_SkAlphaType,
GraphicsJNI::defaultColorSpace());
SkBitmap bitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
bitmap.setInfo(info, bpr);
if (outBuffer.width > 0 && outBuffer.height > 0) {
bitmap.setPixels(outBuffer.bits);
} else {
// be safe with an empty bitmap.
bitmap.setPixels(NULL);
}
Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
nativeCanvas->setBitmap(bitmap);
if (dirtyRectPtr) {
nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
}
if (dirtyRectObj) {
env->SetIntField(dirtyRectObj, gRectClassInfo.left, dirtyRect.left);
env->SetIntField(dirtyRectObj, gRectClassInfo.top, dirtyRect.top);
env->SetIntField(dirtyRectObj, gRectClassInfo.right, dirtyRect.right);
env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, dirtyRect.bottom);
}
// Create another reference to the surface and return it. This reference
// should be passed to nativeUnlockCanvasAndPost in place of mNativeObject,
// because the latter could be replaced while the surface is locked.
sp<Surface> lockedSurface(surface);
lockedSurface->incStrong(&sRefBaseOwner);
return (jlong) lockedSurface.get();
}
- 這里首先會(huì)調(diào)用getSurface獲得與參數(shù)clazz即Surface對(duì)象所對(duì)應(yīng)的C++層的surface。
- 然后設(shè)置更新區(qū)域空骚,這里默認(rèn)是整個(gè)View
- 通過(guò)前面獲取的Surface的lock方法來(lái)獲取一個(gè)圖形緩沖區(qū)
- 新建SKBitmap纺讲,然后獲取canvas,將這個(gè)bitmap放入了Canvas中囤屹。
簡(jiǎn)單來(lái)說(shuō)就是向SurfaceFlinger申請(qǐng)一塊緩沖區(qū)熬甚,然后構(gòu)造一個(gè)SKBitmap對(duì)象,將緩沖區(qū)與SkBitmap關(guān)聯(lián)起來(lái)肋坚,存入Canvas中乡括,然后傳回給上層。
后面就是Canvas的繪制智厌,并不是本文的重點(diǎn)诲泌,繪制完成以后,接下來(lái)就是最后一步了:holder.unlockCanvasAndPost(canvas);.將canvas給解鎖铣鹏,然后post敷扫。
/**
* Posts the new contents of the {@link Canvas} to the surface and
* releases the {@link Canvas}.
*
* @param canvas The canvas previously obtained from {@link #lockCanvas}.
*/
public void unlockCanvasAndPost(Canvas canvas) {
synchronized (mLock) {
checkNotReleasedLocked();
if (mHwuiContext != null) {
mHwuiContext.unlockAndPost(canvas);
} else {
unlockSwCanvasAndPost(canvas);
}
}
}
由于我們并沒(méi)有開啟硬件加速,所以這個(gè)地方會(huì)調(diào)用unlockSwCanvasAndPost方法诚卸。
private void unlockSwCanvasAndPost(Canvas canvas) {
if (canvas != mCanvas) {
throw new IllegalArgumentException("canvas object must be the same instance that "
+ "was previously returned by lockCanvas");
}
if (mNativeObject != mLockedObject) {
Log.w(TAG, "WARNING: Surface's mNativeObject (0x" +
Long.toHexString(mNativeObject) + ") != mLockedObject (0x" +
Long.toHexString(mLockedObject) +")");
}
if (mLockedObject == 0) {
throw new IllegalStateException("Surface was not locked");
}
try {
nativeUnlockCanvasAndPost(mLockedObject, canvas);
} finally {
nativeRelease(mLockedObject);
mLockedObject = 0;
}
}
最終還是會(huì)調(diào)用到C++層中去
/frameworks/base/core/jni/android_view_Surface.cpp
static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj) {
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
if (!isSurfaceValid(surface)) {
return;
}
// detach the canvas from the surface
Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
nativeCanvas->setBitmap(SkBitmap());
// unlock surface
status_t err = surface->unlockAndPost();
if (err < 0) {
doThrowIAE(env);
}
}
這個(gè)方法會(huì)跟局上層傳來(lái)的對(duì)象獲取對(duì)應(yīng)的Surface對(duì)象葵第,此處調(diào)用surface的unlockAndPost:
frameworks/native/libs/gui/Surface.cpp
status_t Surface::unlockAndPost()
{
if (mLockedBuffer == 0) {
ALOGE("Surface::unlockAndPost failed, no locked buffer");
return INVALID_OPERATION;
}
int fd = -1;
status_t err = mLockedBuffer->unlockAsync(&fd);
ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
err = queueBuffer(mLockedBuffer.get(), fd);
ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
mLockedBuffer->handle, strerror(-err));
mPostedBuffer = mLockedBuffer;
mLockedBuffer = 0;
return err;
}
這里會(huì)調(diào)用queueBuffer方法,最終會(huì)調(diào)用mGraphicBufferProducer的queueBuffer:
frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) {
//從input中獲取一些列參數(shù)
input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
&crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
&getFrameTimestamps);
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
//待渲染的一幀
BufferItem item;
//下面是對(duì)這一幀的系列操作
item.mAcquireCalled = mSlots[slot].mAcquireCalled;
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
item.mCrop = crop;
item.mTransform = transform &
~static_cast<uint32_t>(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
item.mTransformToDisplayInverse =
(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
item.mScalingMode = static_cast<uint32_t>(scalingMode);
item.mTimestamp = requestedPresentTimestamp;
item.mIsAutoTimestamp = isAutoTimestamp;
item.mDataSpace = dataSpace;
item.mHdrMetadata = hdrMetadata;
item.mFrameNumber = currentFrameNumber;
item.mSlot = slot;
item.mFence = acquireFence;
item.mFenceTime = acquireFenceTime;
item.mIsDroppable = mCore->mAsyncMode ||
mCore->mDequeueBufferCannotBlock ||
(mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
item.mSurfaceDamage = surfaceDamage;
item.mQueuedBuffer = true;
item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh;
item.mApi = mCore->mConnectedApi;
...代碼省略...
if (frameAvailableListener != NULL) {
//一幀準(zhǔn)備完畢合溺,通知外界
frameAvailableListener->onFrameAvailable(item);
} else if (frameReplacedListener != NULL) {
frameReplacedListener->onFrameReplaced(item);
}
addAndGetFrameTimestamps(&newFrameEventsEntry,etFrameTimestamps ? &output->frameTimestamps : nullptr);
return NO_ERROR;
}
這里主要是對(duì)一幀進(jìn)行了大量的完善處理卒密,然后將這一幀數(shù)據(jù)通知SurfaceFlinger進(jìn)行更新。這樣整個(gè)SurfaceView的繪制流程就結(jié)束了棠赛。
其實(shí)SurfaceView的繪制流程與View是一模一樣的哮奇,只不過(guò)View的繪制流程已經(jīng)在ViewRootImpl上面已經(jīng)封裝好了膛腐,我們只需要在onDraw里面處理canvas就行。而SurfaceView因?yàn)楸旧砭陀蠸urface屏镊,因此不需要聽從ViewRootImpl調(diào)度來(lái)繪制依疼。所以SurfaceView需要自己對(duì)canvas進(jìn)行鎖定和解鎖更新操作痰腮。