上一篇文章說了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;
}
我們這裏調用沒有使用硬件加速(使用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的繪製原理大致流程已經分析完畢。