上一篇文章说了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
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
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
) {
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()) {
if ((mPrivateFlags
& PFLAG_SKIP_DRAW
) == PFLAG_SKIP_DRAW
) {
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 {
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
);
}
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
{
int32_t width
;
int32_t height
;
int32_t stride
;
int32_t format
;
void* bits
;
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;
}
Canvas
* nativeCanvas
= GraphicsJNI
::getNativeCanvas(env
, canvasObj
);
nativeCanvas
->setBitmap(SkBitmap());
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的绘制原理(非硬件加速,其实现在的大部分绘制都采用的硬件加速)大致流程已经分析完毕。