上一篇文章说了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的具体实现中:
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mWindowVisibility = visibility == VISIBLE;
updateRequestedVisibility();
updateSurface();
}
首先调用父类View的onWindowVisibilityChanged
,接着将初始化mWindowVisibility
的值,代表当前SurfaceView是否可见,接着updateRequestedVisibility
更新mRequestedVisible
的值,我们重点要看的是updateSurface
方法,这个方法中会创建SurfaceView自己的surface
。
/** @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创建过程,流程都一样,总结一下:
SurfaceControl
的同时通过nativeCreate
方法到native层做一系列初始化。SurfaceComposerClient
的createSurfaceChecked
函数中通过ISurfaceComposerClient
的Bp端mClient向SurfaceFlinger进程请求创建Surface
,即调用createSurface
函数,而在SurfaceFlinger进程Surface对应的是Layer
。BufferQueueLayer
的过程中,即在BufferQueueLayer的onFirstRef函数中会创建生产者消费者模型架构。Layer
引用和生产者保存到SurfaceControl中,最后将native层SurfaceControl指针保存到java层SurfaceControl。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
这个方法:
@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
的获取:
@Override
public Canvas lockCanvas() {
return internalLockCanvas(null, false);
}
接着会调用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
方法
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
方法:
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
。
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
函数已经分析完毕了,进行一下总结:
lockCanvas
会通过java Surface的lockCanvas
调到native层。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层:
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()
:
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的绘制原理大致流程已经分析完毕。