之前我们讲了Surface的创建过程和SurfaceView的"挖洞"过程
)。这一篇我们就开始讲讲SurfaceView的完整绘制流程
前面也有说过,虽然SurfaceView具有自己的独立的surface,但是毕竟是在一个View Hierarchy中,所以依然还会遵循View的绘制流程。当ViewRootImpl在执行performTravesals的时候,回一次执行performMeasure,performLayout和performDraw。如何测量和布局不是本文的重点,我们直接关注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 里面调用的是draw的方法,draw方法里面先赋值了surface,然后调用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;
}
对这段代码是否比较眼熟?没错,SurfaceView平时在绘制的时候就会让我们在子线程中先通过Surface获取canvas,然后canvas操作完以后需要通过surface的unlockCanvasAndPost进行屏幕刷新。这里面普通的View也是一样的操作,只不过将surface封装了起来,对于开发者来说是不透明的而已。当然这些也不是本文的重点。我们继续往下看,这里最终会调用mView的draw方法,由于DecorView 和ViewGroup都没有重写draw方法,所以直接进入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的主要操作,由于当前仍然是DecorView,所以在执行dispatchDraw 的时候会调用到ViewGroup的dispatchDraw方法,而dispatchDraw方法又调用drawChild,最终还是调用会View的draw方法,不过这次的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条件成立的时候会调用dispatchDraw方法,条件不成立的时候才会draw。之前我们有讲过SurfaceView在初始化的时候就会调用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);
}
}
从这里可以看出来我们如果没有对View设置背景或者前景,然后当我们设置了setWillNotDraw(true)之后,此时mPrivateFlags会和PFLAG_SKIP_DRAW做一次逻辑或。所以SurfaceView最终是不会调用draw方法的,而调用了dispatchDraw方法,那我们在进入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只是简单地将它所占据的区域绘制为黑色,所以我们看到初始化的SurfaceView会是一个黑色的View。
所以SurfaceView无法在onDraw方法上面进行绘制,那么我们就只能自己主动的进行绘制了。又因为SurfaceView不需要告知ViewRootImpl进行CheckThread操作,所以可以直接放在子线程中进行操作:
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
SurfaceHolder holder = mSurfaceView.getHolder();
Canvas canvas = holder.lockCanvas();
//canvas 的绘制操作
holder.unlockCanvasAndPost(canvas);
}
}
}).start();
这里我们细细讲下这三步主要做了什么。
先来看第一步: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对象,我们看下mSurfaceHolder是什么:
private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
...代码省略...
};
其实就是在SurfaceView初始化的时候就会初始化SurfaceHolder。
那么接下来看第二步:holder.lockCanvas();
SurfaceHolder本身是一个接口,但是它在SurfaceView里面初始化的时候实现了里面的方法:
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会调用internalLockCanvas方法,传入的参数是null和false,前者传null就表示要获取整块的canvas,后者传false是代表不要硬件加速。最后会调用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;
}
}
会发现这个时候需要通过C++层将canvas传回来。
/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) {
//获取脏数据区域,也就是要更新的范围,这里传了null,所以dirtyRect不设置区域默认整个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();
}
- 这里首先会调用getSurface获得与参数clazz即Surface对象所对应的C++层的surface。
- 然后设置更新区域,这里默认是整个View
- 通过前面获取的Surface的lock方法来获取一个图形缓冲区
- 新建SKBitmap,然后获取canvas,将这个bitmap放入了Canvas中。
简单来说就是向SurfaceFlinger申请一块缓冲区,然后构造一个SKBitmap对象,将缓冲区与SkBitmap关联起来,存入Canvas中,然后传回给上层。
后面就是Canvas的绘制,并不是本文的重点,绘制完成以后,接下来就是最后一步了: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);
}
}
}
由于我们并没有开启硬件加速,所以这个地方会调用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;
}
}
最终还是会调用到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);
}
}
这个方法会跟局上层传来的对象获取对应的Surface对象,此处调用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;
}
这里会调用queueBuffer方法,最终会调用mGraphicBufferProducer的queueBuffer:
frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) {
//从input中获取一些列参数
input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
&crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
&getFrameTimestamps);
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
//待渲染的一帧
BufferItem item;
//下面是对这一帧的系列操作
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) {
//一帧准备完毕,通知外界
frameAvailableListener->onFrameAvailable(item);
} else if (frameReplacedListener != NULL) {
frameReplacedListener->onFrameReplaced(item);
}
addAndGetFrameTimestamps(&newFrameEventsEntry,etFrameTimestamps ? &output->frameTimestamps : nullptr);
return NO_ERROR;
}
这里主要是对一帧进行了大量的完善处理,然后将这一帧数据通知SurfaceFlinger进行更新。这样整个SurfaceView的绘制流程就结束了。
其实SurfaceView的绘制流程与View是一模一样的,只不过View的绘制流程已经在ViewRootImpl上面已经封装好了,我们只需要在onDraw里面处理canvas就行。而SurfaceView因为本身就有Surface,因此不需要听从ViewRootImpl调度来绘制。所以SurfaceView需要自己对canvas进行锁定和解锁更新操作。