Android的UI顯示原理之Surface的渲染
本文內容接著上一篇文章 Android的UI顯示原理之Surface的建立 繼續來看 Surface
渲染的大致過程。
這裡說的 Surface的渲染
其實就是 ViewRootImpl
的渲染。因此我們從 ViewRootImpl.draw()
來看一下它的渲染邏輯。這個方法最終會呼叫到 ViewRootImpl.drawSoftward()
:
private boolean drawSoftware(Surface surface, AttachInfo attachInfo,...) { // Draw with software renderer. final Canvas canvas; ... canvas = mSurface.lockCanvas(dirty);//step 1 ... mView.draw(canvas);//setp 2 ... mSurface.unlockCanvasAndPost(canvas);//step 3 }
mView
是 ViewRootImpl
的根 View
。 mSurface
即為上一篇文章分析的所建立的 Surface
,它的實際物件在nativie層。
上面3步大致描繪了 ViewRootImpl
的繪製原理,本文就逐一分析這3步,來大致瞭解 ViewRootImpl
的渲染邏輯。
mSurface.lockCanvas()
public Canvas lockCanvas(Rect inOutDirty){ ... mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty); return mCanvas; }
這個方法會直接呼叫到native方法 nativeLockCanvas(mNativeObject, mCanvas, inOutDirty)
。
mNativeObject
就是 Surface
建立時對應的native Surface
指標, inOutDirty
指明瞭 lock
的區域。
android_view_Surface.cpp
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; //獲取 lock 區域 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); ... sp<Surface> lockedSurface(surface); return (jlong) lockedSurface.get(); //返回surface指標 }
這個方法主要新建了一個 Rect
物件,確定要 lock
的區域的引數,然後呼叫 surface->lock(&outBuffer, dirtyRectPtr)
:
Surface.cpp
status_t Surface::lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds){ ANativeWindowBuffer* out; int fenceFd = -1; status_t err = dequeueBuffer(&out, &fenceFd);//從 GraphicBufferProduce 中 拿出來一個 buffer sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));// 構建一個 backbuffer status_t res = backBuffer->lockAsync(...); }
可以看出 lock
方法,首先通過 dequeueBuffer
來獲取一個 ANativeWindowBuffer
,然後利用它構造一個 GraphicBuffer
,它被稱為 backBuffer
,然後呼叫它的 backBuffer->lockAsync(...)
,那麼怎麼獲取一個 ANativeWindowBuffer
呢?
dequeueBuffer()
Surface.cpp
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { ... status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight, reqFormat, reqUsage, &mBufferAge, enableFrameTimestamps ? &frameTimestamps : nullptr); ...經過一系列的操作,buffer最終會指向&buf }
我們上一篇文章中已經介紹過了 GraphicBufferProducer
,它是一個 Layer
的 graphic buffer producer
, Layer
在繪製時,會從 GraphicBufferProducer
取出一個 GraphicBuffer
來繪製。所以可以理解為 GraphicBufferProducer
存放著一個 Layer
待繪製的一幀。 dequeueBuffer()
所做的事情就是:從 GraphicBufferProducer
取出一個 GraphicBuffer
。
至於 backBuffer->lockAsync(...)
所做的操作就不細看了,可以猜想就是把這個 GraphicBuffer
鎖上,保證一個 GraphicBuffer
繪製操作的不可重入。
綜上, surface.lockCanvas()
的主要邏輯可以使用下面這張圖來表示:

SurfaceLockCanvas.png
即 mSurface.lockCanvas
最終是lock了一個 GraphicBuffer
。繼續看 mView.draw(canvas)
:
mView.draw(canvas)
View.draw(canvas)
所做的事情其實就是:根據 View
的狀態來把帶繪製的資料儲存到 Canvas
。對 Canvas
我們到最後再來看一下它和 Surface
的關係。
mSurface.unlockCanvasAndPost(canvas)
在 canvas
的繪製資料準備ok後, Surface
就可以開始繪製了,而繪製操作的發起點就是 Surface.unlockCanvasAndPost()
方法,這個方法會呼叫到:
private void unlockSwCanvasAndPost(Canvas canvas) { ... try { nativeUnlockCanvasAndPost(mLockedObject, canvas); //mLockedObject其實就是native的那個surface } finally { nativeRelease(mLockedObject); mLockedObject = 0; } }
繼續看 nativeUnlockCanvasAndPost()
android_view_Surface.cpp
static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,jlong nativeObject, jobject canvasObj) { sp<Surface> surface(reinterpret_cast<Surface*>(nativeObject)); ... // detach the canvas from the surface Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);// 把java canvas指標轉化為native 指標 nativeCanvas->setBitmap(SkBitmap()); // unlock surface status_t err = surface->unlockAndPost(); }
即呼叫了 surface->unlockAndPost()
:
Surface.cpp
status_t Surface::unlockAndPost() { ... int fd = -1; status_t err = mLockedBuffer->unlockAsync(&fd); err = queueBuffer(mLockedBuffer.get(), fd); mPostedBuffer = mLockedBuffer; mLockedBuffer = 0; return err; }
這裡 mLockedBuffer
其實就是前面的 backBuffer
。 mLockedBuffer->unlockAsync(&fd)
的操作其實很簡單,就是解除 GraphicBuffer
的lock狀態,主要看一下 queueBuffer(mLockedBuffer.get(), fd)
Surface.cpp
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { ... int i = getSlotFromBufferLocked(buffer); ... IGraphicBufferProducer::QueueBufferOutput output; IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, static_cast<android_dataspace>(mDataSpace), crop, mScalingMode, mTransform ^ mStickyTransform, fence, mStickyTransform, mEnableFrameTimestamps); ... status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); ... mQueueBufferCondition.broadcast(); return err; }
對於 input
和 output
這裡先不做細追, 我猜測 input
中應該包含繪製的資料 。但 getSlotFromBufferLocked(buffer)
是幹什麼的呢?它其實就是獲取真正的 GraphicBuffer
的在 mSlots
集合中真正的index:
Surface.cpp
int Surface::getSlotFromBufferLocked(android_native_buffer_t* buffer) const { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (mSlots[i].buffer != NULL && mSlots[i].buffer->handle == buffer->handle) { return i; } } return BAD_VALUE; }
對於 mSlots
集合,可以認為他就是按順序儲存 GraphicBuffer
的陣列即可:
Surface.h
// mSlots stores the buffers that have been allocated for each buffer slot. // It is initialized to null pointers, and gets filled in with the result of // IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. BufferSlot mSlots[NUM_BUFFER_SLOTS];
繼續看 mGraphicBufferProducer->queueBuffer(i, input, &output)
, 通過上一篇文章已經知道 mGraphicBufferProducer
是 BufferQueueProducer
的例項:
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的一系列賦值操作 item.mAcquireCalled = mSlots[slot].mAcquireCalled; item.mGraphicBuffer = mSlots[slot].mGraphicBuffer; //根據slot獲取GraphicBuffer。 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; ... if (frameAvailableListener != NULL) { frameAvailableListener->onFrameAvailable(item); //item是一個frame,準備完畢,要通知外界 } else if (frameReplacedListener != NULL) { frameReplacedListener->onFrameReplaced(item); } addAndGetFrameTimestamps(&newFrameEventsEntry,etFrameTimestamps ? &output->frameTimestamps : nullptr); return NO_ERROR; }
其實從 queueBuffer()
可以看出, mGraphicBufferProducer
中存放的都是可以被渲染的 GraphicBuffer
,這個 buffer
可能被渲染完畢,也可能處於待渲染狀態。
queueBuffer
的主要操作是根據輸入引數完善一個 BufferItem
,然後通知外界去繪製這個 BufferItem
。這裡這個 frameAvailableListener
是什麼呢?有興趣的同學可以去跟一下, 不過最終回到 BufferLayer.onFrameAvailable()
:
BufferLayer.cpp
// --------------------------------------------------------------------------- // Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener // --------------------------------------------------------------------------- void BufferLayer::onFrameAvailable(const BufferItem& item) { ... mFlinger->signalLayerUpdate(); }
這個方法直接呼叫了 mFlinger->signalLayerUpdate()
,看樣是要讓 SurfaceFlinger
來渲染了:
SurfaceFlinger.cpp
void SurfaceFlinger::signalLayerUpdate() { mEventQueue->invalidate(); }
至於 SurfaceFlinger
是如何渲染的,本文就不繼續追蹤了。用下面這張圖總結一下 mSurface.unlockCanvasAndPost(canvas)
:

SurfaceUnlockCanvasAndPost(canvas).png
Canvas與Surface
Canvas
是一個繪圖的工具類,其API提供了一系列繪圖指令供開發者使用。根據繪製加速模式的不同, Canvas
有軟體 Canvas
與硬體 Canvas
只分。 Canvas
的繪圖指令可以分為兩個部分:
- 繪製指令:這些最常用的指令由一系列名為
drawXXX
的方法提供。他們用來實現實際的繪製行為,例如繪製點、線、圓以及方塊等。 - 輔助指令:這些用於提供輔助功能的指令將會影響後續繪製指令的效果,如設定變換、裁剪區域等。
Canvas
還提供了save
與resotore
用於撤銷一部分輔助指令的效果。
對於軟體 Canvas
來說,其繪製目標是一個位圖 Bitmap
。在 Surface.unlockAndPost
時,這個 Bitmap
所描述的內容會反映到 Surface
的 Buffer
中。可以用下面這張圖來表示:

Canvas與Surface.png
最後:
歡迎關注我的 Android進階計劃 看更多幹貨
歡迎關注我的微信公眾號:susion隨心

微信公眾號.jpeg