AndroidQ 图形系统(10)SurfaceView实现原理之surface创建和绘制原理

    技术2022-07-12  85

    上一篇文章说了SurfaceView默认Z-order是小于主窗口的,为了能够显示出来,需要以自身所占矩形区域在主窗口设置透明区域,这是在SurfaceView的回调onAttachedToWindow中实现的,本篇接着看SurfaceView另一个回调onWindowVisibilityChanged。

    首先还是贴出上一篇分析的ViewRootImpl的performTraversals方法部分代码:

    private void performTraversals() { final View host = mView; ...... if (mFirst) { ... host.dispatchAttachedToWindow(mAttachInfo, 0); ... } ... if (viewVisibilityChanged) { ... host.dispatchWindowVisibilityChanged(viewVisibility); ... } ..... performMeasure(); ... performLayout(); ... performDraw(); ... }

    其实这里调用host.dispatchWindowVisibilityChanged和前面host.dispatchAttachedToWindow流程都类似的,就是遍历View树,让所有View都能执行到dispatchWindowVisibilityChanged,但我们一般不会重写View的dispatchWindowVisibilityChanged方法的,而是重写onWindowVisibilityChanged方法: View.java

    /** * Dispatch a window visibility change down the view hierarchy. * ViewGroups should override to route to their children. * * @param visibility The new visibility of the window. * * @see #onWindowVisibilityChanged(int) */ public void dispatchWindowVisibilityChanged(@Visibility int visibility) { onWindowVisibilityChanged(visibility); }

    所以这个遍历流程我们就不看了,直接来到SurfaceView的具体实现中:

    SurfaceView.onWindowVisibilityChanged

    @Override protected void onWindowVisibilityChanged(int visibility) { super.onWindowVisibilityChanged(visibility); mWindowVisibility = visibility == VISIBLE; updateRequestedVisibility(); updateSurface(); }

    首先调用父类View的onWindowVisibilityChanged,接着将初始化mWindowVisibility的值,代表当前SurfaceView是否可见,接着updateRequestedVisibility更新mRequestedVisible的值,我们重点要看的是updateSurface方法,这个方法中会创建SurfaceView自己的surface。

    updateSurface

    /** @hide */ protected void updateSurface() { ... ViewRootImpl viewRoot = getViewRootImpl(); ... int myWidth = mRequestedWidth; if (myWidth <= 0) myWidth = getWidth(); int myHeight = mRequestedHeight; if (myHeight <= 0) myHeight = getHeight(); final float alpha = getFixedAlpha(); final boolean formatChanged = mFormat != mRequestedFormat; final boolean visibleChanged = mVisible != mRequestedVisible; final boolean alphaChanged = mSurfaceAlpha != alpha; final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged) && mRequestedVisible; final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight; final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility; boolean redrawNeeded = false; if (creating || formatChanged || sizeChanged || visibleChanged || (mUseAlpha && alphaChanged) || windowVisibleChanged) { getLocationInWindow(mLocation); ... try { final boolean visible = mVisible = mRequestedVisible; mWindowSpaceLeft = mLocation[0]; mWindowSpaceTop = mLocation[1]; mSurfaceWidth = myWidth; mSurfaceHeight = myHeight; mFormat = mRequestedFormat; mLastWindowVisibility = mWindowVisibility; mScreenRect.left = mWindowSpaceLeft; mScreenRect.top = mWindowSpaceTop; mScreenRect.right = mWindowSpaceLeft + getWidth(); mScreenRect.bottom = mWindowSpaceTop + getHeight(); mScreenRect.offset(surfaceInsets.left, surfaceInsets.top); if (creating) { viewRoot.createBoundsSurface(mSubLayer); mSurfaceSession = new SurfaceSession(); mDeferredDestroySurfaceControl = mSurfaceControl; updateOpaqueFlag(); final String name = "SurfaceView - " + viewRoot.getTitle().toString(); mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) .setName(name) .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0) .setBufferSize(mSurfaceWidth, mSurfaceHeight) .setFormat(mFormat) .setParent(viewRoot.getSurfaceControl()) .setFlags(mSurfaceFlags) .build(); mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession) .setName("Background for -" + name) .setOpaque(true) .setColorLayer() .setParent(mSurfaceControl) .build(); } else if (mSurfaceControl == null) { return; } boolean realSizeChanged = false; mSurfaceLock.lock(); try { mDrawingStopped = !visible; if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Cur surface: " + mSurface); SurfaceControl.openTransaction(); try { mSurfaceControl.setLayer(mSubLayer); if (mViewVisibility) { mSurfaceControl.show(); } else { mSurfaceControl.hide(); } updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl()); if (mUseAlpha) { mSurfaceControl.setAlpha(alpha); mSurfaceAlpha = alpha; } ... if (sizeChanged || creating || !mRtHandlingPositionUpdates) { mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top); mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth, 0.0f, 0.0f, mScreenRect.height() / (float) mSurfaceHeight); ... mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight); } mSurfaceControl.setCornerRadius(mCornerRadius); if (sizeChanged && !creating) { mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight); } } finally { SurfaceControl.closeTransaction(); } if (sizeChanged || creating) { redrawNeeded = true; } mSurfaceFrame.left = 0; mSurfaceFrame.top = 0; if (translator == null) { mSurfaceFrame.right = mSurfaceWidth; mSurfaceFrame.bottom = mSurfaceHeight; } else { float appInvertedScale = translator.applicationInvertedScale; mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f); mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f); } final int surfaceWidth = mSurfaceFrame.right; final int surfaceHeight = mSurfaceFrame.bottom; realSizeChanged = mLastSurfaceWidth != surfaceWidth || mLastSurfaceHeight != surfaceHeight; mLastSurfaceWidth = surfaceWidth; mLastSurfaceHeight = surfaceHeight; } finally { mSurfaceLock.unlock(); } try { redrawNeeded |= visible && !mDrawFinished; SurfaceHolder.Callback[] callbacks = null; final boolean surfaceChanged = creating; if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) { mSurfaceCreated = false; if (mSurface.isValid()) { if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "visibleChanged -- surfaceDestroyed"); callbacks = getSurfaceCallbacks(); for (SurfaceHolder.Callback c : callbacks) { c.surfaceDestroyed(mSurfaceHolder); } if (mSurface.isValid()) { mSurface.forceScopedDisconnect(); } } } if (creating) { mSurface.copyFrom(mSurfaceControl); } if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O) { mSurface.createFrom(mSurfaceControl); } if (visible && mSurface.isValid()) { if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { mSurfaceCreated = true; mIsCreating = true; if (callbacks == null) { callbacks = getSurfaceCallbacks(); } for (SurfaceHolder.Callback c : callbacks) { c.surfaceCreated(mSurfaceHolder); } } if (creating || formatChanged || sizeChanged || visibleChanged || realSizeChanged) { if (callbacks == null) { callbacks = getSurfaceCallbacks(); } for (SurfaceHolder.Callback c : callbacks) { c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight); } } if (redrawNeeded) { if (callbacks == null) { callbacks = getSurfaceCallbacks(); } mPendingReportDraws++; viewRoot.drawPending(); SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::onDrawFinished); sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); } } } finally { mIsCreating = false; if (mSurfaceControl != null && !mSurfaceCreated) { mSurface.release(); releaseSurfaces(); } } } catch (Exception ex) { Log.e(TAG, "Exception configuring surface", ex); } } else { final boolean positionChanged = mWindowSpaceLeft != mLocation[0] || mWindowSpaceTop != mLocation[1]; final boolean layoutSizeChanged = getWidth() != mScreenRect.width() || getHeight() != mScreenRect.height(); if (positionChanged || layoutSizeChanged) { // Only the position has changed mWindowSpaceLeft = mLocation[0]; mWindowSpaceTop = mLocation[1]; mLocation[0] = getWidth(); mLocation[1] = getHeight(); mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop, mWindowSpaceLeft + mLocation[0], mWindowSpaceTop + mLocation[1]); if (translator != null) { translator.translateRectInAppWindowToScreen(mScreenRect); } if (mSurfaceControl == null) { return; } if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) { try { setParentSpaceRectangle(mScreenRect, -1); } catch (Exception ex) { Log.e(TAG, "Exception configuring surface", ex); } } } } }

    删了一些代码,看着代码挺多,其实很多代码都是获取SurfaceView的信息相关,宽高,透明度,坐标等,还有很多是surface创建之后需要设置的信息,可以看到有很多setXXX方法,这个方法的核心逻辑还是创建SurfaceControl和Surface,创建方式和APP一样的,APP的SurfaceControl和Surface是在ViewRootImpl的performTraversals方法中创建的,等下再说,先来看这个方法。

    首先拿到SurfaceView宽高,可以通过外部调用setFixedSize设置,如果没设置就拿SurfaceView测量到的,这里提一下,如果SurfaceView的宽高测量模式为EXACTLY,则setFixedSize不会生效,这是SurfaceView自定义的测量方法onMeasure中调用了View的resolveSizeAndState决定的,具体可以自己去看,很简单,接着就是获取SurfaceView一系列信息,透明度,格式是否改变,可见性是否改变等等,这些值作为是否往下走的判断依据。

    getLocationInWindow获取SurfaceView左上角坐标,接着一系列赋值,SurfaceView坐标啊,宽高,格式等,重点是SurfaceControl的创建,我在AndroidQ 图形系统(1)Surface与SurfaceControl创建分析已经分析过APP的Surface与SurfaceControl创建过程,流程都一样,总结一下:

    首先应用进程会new一个java层SurfaceControl,什么都没做,然后传递到WMS进程,因为SurfaceControl在AIDL中是out类型,所以在WMS进程赋值。WMS在创建java层SurfaceControl的同时通过nativeCreate方法到native层做一系列初始化。在SurfaceComposerClient的createSurfaceChecked函数中通过ISurfaceComposerClient的Bp端mClient向SurfaceFlinger进程请求创建Surface,即调用createSurface函数,而在SurfaceFlinger进程Surface对应的是Layer。在第一次创建Layer的子类BufferQueueLayer的过程中,即在BufferQueueLayer的onFirstRef函数中会创建生产者消费者模型架构。SurfaceFlinger进程的任务完成之后会直接new一个SurfaceControl,并将SurfaceFlinger进程创建的Layer引用和生产者保存到SurfaceControl中,最后将native层SurfaceControl指针保存到java层SurfaceControl。native层SurfaceControl创建好了之后就可以通过此对象创建native层的Surface对象,即调用mSurface.copyFrom(mSurfaceControl),最后将native层Surface指针保存到java层Surface,最终java层和native层的Surface和SurfaceControl都创建完毕。

    SurfaceView中创建surface也是同样的流程,大部分事情都是在native层做的,最重要的就是创建了SurfaceFlinger进程的生产者-消费者模式架构以及Layer。

    SurfaceControl创建好了之后会调用很多setXXX方法,如setLayer,setAlpha,setPosition,setBufferSize等,这些信息最终都会设置到SurfaceFlinger的Layer中去,对于应用层来说其实就是SurfaceView的信息。

    接着来看:

    if (creating) { mSurface.copyFrom(mSurfaceControl); }

    通过copyFrom会到native层创建native层的Surface,并将指针保存在java层Surface中,这个方法执行完也宣告SurfaceView的Surface创建完毕。

    再来看:

    if (visible && mSurface.isValid()) { if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { mSurfaceCreated = true; mIsCreating = true; if (callbacks == null) { callbacks = getSurfaceCallbacks(); } for (SurfaceHolder.Callback c : callbacks) { c.surfaceCreated(mSurfaceHolder); } }

    getSurfaceCallbacks会获取所有添加的SurfaceHolder.Callback,遍历他们调用surfaceCreated回调方法通知开发者SurfaceView的Surface创建完毕,我们可以拿到Surface进行绘制了。

    再看下surfaceChanged回调的代码:

    if (creating || formatChanged || sizeChanged || visibleChanged || realSizeChanged) { if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "surfaceChanged -- format=" + mFormat + " w=" + myWidth + " h=" + myHeight); if (callbacks == null) { callbacks = getSurfaceCallbacks(); } for (SurfaceHolder.Callback c : callbacks) { c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight); } }

    上面这几个条件都会引起surfaceChanged。

    接着看surfaceDestroyed的回调,主要就是SurfaceView变得不可见时调用:

    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) { mSurfaceCreated = false; if (mSurface.isValid()) { if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "visibleChanged -- surfaceDestroyed"); callbacks = getSurfaceCallbacks(); for (SurfaceHolder.Callback c : callbacks) { c.surfaceDestroyed(mSurfaceHolder); } if (mSurface.isValid()) { mSurface.forceScopedDisconnect(); } } }

    到此SurfaceView自己的Surface创建流程以及SurfaceHolder.Callback回调时机已经清楚了。这些完成之后就会对SurfaceView进行测量,布局与绘制了,虽然SurfaceView不会走onDraw流程,但是它重写了View的dispatchDraw方法,所以还是会调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 clearSurfaceViewPort(canvas); } } super.dispatchDraw(canvas); } private void clearSurfaceViewPort(Canvas canvas) { if (mCornerRadius > 0f) { canvas.getClipBounds(mTmpRect); canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom, mCornerRadius, mCornerRadius, mRoundedViewportPaint); } else { canvas.drawColor(0, PorterDuff.Mode.CLEAR); } }

    这个方法最终其实是调用了canvas.drawColor给SurfaceView画了一个黑色背景,这里的canvas是APP的,并不是SurfaceView的。

    接着我们要来看看SurfaceView的绘制原理,首先来看一段最简单的使用SurfaceView绘制的代码:

    package com.example.surfaceviewdemo; import androidx.appcompat.app.AppCompatActivity; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Bundle; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; public class MainActivity extends AppCompatActivity { private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private Paint mPaint; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSurfaceView = findViewById(R.id.surfaceView); mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.addCallback(new MyHolderCallback()); mPaint = new Paint(); } class MyHolderCallback implements SurfaceHolder.Callback{ @Override public void surfaceCreated(final SurfaceHolder holder) { Log.d("dongjiao","surfaceCreated...."); new Thread(new Runnable() { @Override public void run() { mPaint.setColor(Color.RED); Canvas canvas = holder.lockCanvas(); canvas.drawLine(0,100,400,100,mPaint); holder.unlockCanvasAndPost(canvas); } }).start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d("dongjiao","surfaceChanged...."); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d("dongjiao","surfaceDestroyed...."); } } }

    在子线程中绘制一条直线: 我们可以发现好像用SurfaceView进行绘制和用普通View绘制的区别不大,SurfaceView除了能够在子线程绘制外其他都差不多,为什么在SurfaceView中需要自己手动获取canvas,而普通View不需要,想一下普通的自定义View的onDraw方法中的canvas哪来的,来自ViewRootImpl中的drawSoftware方法(非硬件加速情况):

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty, Rect surfaceInsets) { ...... canvas = mSurface.lockCanvas(dirty); .... mView.draw(canvas); .... surface.unlockCanvasAndPost(canvas); ..... }

    我们可以发现,普通View的canvas和SurfaceView的canvas获取方式是一样的,不一样的只是Surface,普通View由系统帮我们获取之后通过DecorView传递到View树每一个View的onDraw方法中,因为SurfaceView有自己的Surface,所以需要手动通过自己的Surface获取自己单独的canvas,我们接下来就来看看SurfaceView中这小小的一段绘制代码到底做了什么事。

    绘制的代码就只有三句:

    Canvas canvas = holder.lockCanvas(); canvas.drawLine(0,100,400,100,mPaint); holder.unlockCanvasAndPost(canvas);

    首先来看canvas的获取:

    holder.lockCanvas

    @Override public Canvas lockCanvas() { return internalLockCanvas(null, false); }

    接着会调用SurfaceHolder的internalLockCanvas方法:

    SurfaceHolder.internalLockCanvas

    private Canvas internalLockCanvas(Rect dirty, boolean hardware) { mSurfaceLock.lock(); Canvas c = null; if (!mDrawingStopped && mSurfaceControl != null) { try { if (hardware) { c = mSurface.lockHardwareCanvas(); } else { c = mSurface.lockCanvas(dirty); } } catch (Exception e) { } } if (c != null) { mLastLockTime = SystemClock.uptimeMillis(); return c; } ...... mSurfaceLock.unlock(); return null; }

    我们这里调用没有使用硬件加速(在xml中关闭了硬件加速,使用CPU渲染)的绘制,所以走mSurface.lockCanvas,dirty传递的是null,接着调到Surface的lockCanvas方法

    Surface.lockCanvas

    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; } }

    mLockedObject是native层返回的Surface的指针,是根据mNativeObject重新创建的一个native层Surface,接着来看看nativeLockCanvas方法:

    nativeLockCanvas

    static jlong nativeLockCanvas(JNIEnv* env, jclass clazz, jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) { sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject)); ...... Rect dirtyRect(Rect::EMPTY_RECT); Rect* dirtyRectPtr = NULL; if (dirtyRectObj) { 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); 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(); }

    首先如果我们调用lockCanvas时指定了绘制的脏区域(对于非脏区域就直接复制上一个缓冲区的),则将此区域大小赋值给dirtyRect,让dirtyRectPtr指向此区域,这里我们传递的是null所以不会走这一步,接下来就是最重要的步骤了surface->lock,传递了两个参数,outBuffer类型为ANativeWindow_Buffer,其实可以理解为它就是代表GraphicBuffer,来看看ANativeWindow_Buffer结构体:

    typedef struct ANativeWindow_Buffer { /// The number of pixels that are shown horizontally. int32_t width; /// The number of pixels that are shown vertically. int32_t height; /// The number of *pixels* that a line in the buffer takes in /// memory. This may be >= width. int32_t stride; /// The format of the buffer. One of AHardwareBuffer_Format. int32_t format; /// The actual bits. void* bits; /// Do not touch. uint32_t reserved[6]; } ANativeWindow_Buffer;

    这个结构体中的宽高,格式等就是surface->lock申请到的GraphicBuffer的宽高,格式,最重要的是bits,它保存了GraphicBuffer的地址,GraphicBuffer中的数据比较大,在进程间传递是不现实的,所以传递的是句柄。

    surface->lock主要目的就是向BufferQueue申请图形缓冲区GraphicBuffer,首次调用时GraphicBuffer将被分配并初始化为零,GraphicBuffer的内存分配使用的Gralloc,Gralloc中有两个重要的辅助,一个负责GraphicBuffer内存分配的GraphicBufferAllocator,另一个负责GraphicBuffer内存映射的GraphicBufferMapper。

    Surface->lock

    status_t Surface::lock( ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { ...... status_t err = dequeueBuffer(&out, &fenceFd); ...... const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); const bool canCopyBack = (frontBuffer != nullptr && backBuffer->width == frontBuffer->width && backBuffer->height == frontBuffer->height && backBuffer->format == frontBuffer->format); ..... sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); ...... void* vaddr; status_t res = backBuffer->lockAsync( GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, newDirtyRegion.bounds(), &vaddr, fenceFd); ....... if (res != 0) { err = INVALID_OPERATION; } else { mLockedBuffer = backBuffer; outBuffer->width = backBuffer->width; outBuffer->height = backBuffer->height; outBuffer->stride = backBuffer->stride; outBuffer->format = backBuffer->format; outBuffer->bits = vaddr; } }

    可以看一下,Surface->lock核心就是上面这点代码,这里用到了双重缓冲,我们申请的GraphicBuffer其实是backBuffer,还有一个frontBuffer正在显示,当我们在backBuffer绘制好数据之后调用unlockAndPost提交时会交换两个buffer,双重buffer使画面更加流畅。

    dequeueBuffer函数最终会向BufferQueue申请GraphicBuffer,可参考AndroidQ 图形系统(3)dequeueBuffer函数分析, 申请的大致步骤是:优先获取已经绑定了GraphicBuffer且状态为FREE的BufferSlot,如果没有,再获取没有绑定GraphicBuffer且状态为FREE的BufferSlot,并设置flag为BUFFER_NEEDS_REALLOCATION,然后会通过Gralloc对GraphicBuffer进行内存分配和映射。

    接着dequeueBuffer申请到GraphicBuffer之后,通过lockAsync最终调到GraphicBufferMapper中得到GraphicBuffer的vaddr,最后给开始传递过来的ANativeWindow_Buffer outBuffer赋值,outBuffer就保存了GraphicBuffer的相关信息,并且有了vaddr之后就可以对GraphicBuffer进行操作了。

    我们再回到nativeLockCanvas看surface->lock之后的操作,创建了SkImageInfo和SkBitmap,这两个是Skia 2D图形库相关的东西,并且关联上了ANativeWindow_Buffer,然后通过java层canvas得到native层canvas,并调用setBitmap将SkBitmap设置到canvas中,所以我们View中使用canvas API时其实使用的是Skia库画在了SkBitmap上,最终到了GraphicBuffer,目前Android系统已经很少使用Skia绘图了,更多的是OpenGL ES 或 Vulkan 。 再接着看Surface->lock最后一点代码,因为我们没有指定脏区域,所以这部分代码不会走,最后根据nativeObject得到的surface在拷贝一个lockedSurface返回给java层。

    到此SurfaceHolder的lockCanvas函数已经分析完毕了,进行一下总结:

    SurfaceHolder的lockCanvas会通过java Surface的lockCanvas调到native层。在native层最重要的一件事情就是通过native Surface的lock函数,最终通过dequeueBuffer函数向BufferQueue申请GraphicBuffer,首次申请GraphicBuffer是一件很复杂的事情,需要Gralloc模块帮忙分配内存和映射内存。GraphicBuffer申请完成之后会创建Skia库相关的SkImageInfo和SkBitmap,SkImageInfo的相关信息就是从申请到的GraphicBuffer中获取的,通过setInfo将GraphicBuffer和SkBitmap关联,同时还将SkBitmap设置到Canvas中,这样上层java使用Canvas API底层依靠的是Skia进行绘制。

    再回到SurfaceView中的一小段绘制代码:

    Canvas canvas = holder.lockCanvas(); canvas.drawLine(0,100,400,100,mPaint); holder.unlockCanvasAndPost(canvas);

    lockCanvas结束之后,使用Canvas APIdrawLine方法进行绘制,底层通过Skia完成绘制之后调用unlockCanvasAndPost将绘制结果提交,我们再来简单看看unlockCanvasAndPost方法,这个方法会同样是调到native层:

    nativeUnlockCanvasAndPost

    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->unlockAndPost():

    Surface->unlockAndPost

    status_t Surface::unlockAndPost() { if (mLockedBuffer == nullptr) { 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 = nullptr; return err; }

    可以看到此函数中调用了queueBuffer函数,这个函数会将绘制好的图形缓冲区放入BufferQueue中,然后通知SurfaceFlinger取, AndroidQ 图形系统(4)queueBuffer函数分析 ,通知的过程就是请求一个SurfaceFlinger进程的Vsync,待收到Vsync之后就可以拿到图形缓冲区进行合成了。

    此函数最后mPostedBuffer = mLockedBuffer将backBuffer交换到前台。

    到此SurfaceView的绘制原理(非硬件加速,其实现在的大部分绘制都采用的硬件加速)大致流程已经分析完毕。

    Processed: 0.012, SQL: 9