前言
本篇是Android绘制流程的第三篇,在Window连接、Surface创建中介绍了应用是如何与SurfaceFlinger
关联,并且创建了可以绘制的图层。
接下来就接着向下分析,看看视图是如何绘制是如何在开辟出来的Surface
上绘制的。
作为复习记录总结,同样只是进行一个粗略的、流程上的探究。
这里暂且先忽略一些难懂的细节,以建立起一个大致的概念为目标。
如果有误,欢迎指出,共同进步
源码解析
既然已经有了可以绘制的图层,接下来自然就到熟悉的视图绘制三剑客——
measure
、layout
、draw
了以下代码基于 Android 11 (Android R),限于篇幅,只保留重要核心代码。
1 measure
首先是测量视图树内所有View的尺寸,也就是performTraversals
调用的performMeasure
方法。
PS:当然,实际上并不止这一个地方调用
performMeasure
方法。
// ==============android.view.ViewRootImpl================
private void performTraversals() {
// 最外层的测量模式与尺寸 (默认为Window屏幕大小)
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
...
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
还记得
ViewRootImpl
内的mView
是 什么吗?在第一篇中,我们在创建ViewRootImpl后,通过setView
方法赋值的就是DecorView
。而
DecorView
继承自FrameLayout
是个ViewGroup。
关于在
measure
过程中的重要属性MeasureSpec
:
MeasureSpec
是32位int类型的整数值。在2进制中的高2位表示SpecMode(测量模式 ),低30位表示SpecSize(测量模式下的规格大小),由两个值通过位运算组合而成。
其中SpecMode有3类值:
UNSPECIFIED
父容器不对子View
限制尺寸。EXACTLY
精确大小,为SpecSize
值,一般在设置了match_parent
和具体数值时使用这种模式。AT_MOST
父容器指定了一个可用大小即SpecSize
,View
的大小不能大于这个值,一般在设置了wrap_content
时设置了。对于最外层
DecorView
的MeasureSpec
,则是默认通过Window的大小在getRootMeasureSpec
内获取。
// ==============android.view.ViewRootImpl================ private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
随后就是对DecorView
这个ViewGroup
的拆View套娃的环节,遍历视图下的所有子View,直到最里面View完成测量。
由于
ViewGroup
是个抽象类,onMeasure
操作都在子类中进行实现。这里就以FrameLayout
为例进行分析。
// ========== android.view.View ==============
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
//判断是否是布局
boolean optical = isLayoutModeOptical(this);
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
// ========== android.widget.FrameLayout ================
public class FrameLayout extends ViewGroup {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
...
for (int i = 0; i < count; i++) {
//遍历视图树下的所有子View
final View child = getChildAt(i);
//过滤GONE的View
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//执行子View的测量工作
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
...
}
}
// 添加Padding的尺寸
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
//比较最小尺寸与padding尺寸
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// 比较ViewGroup内部设置的Drawable尺寸
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
// 设置自身的尺寸
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
int getPaddingLeftWithForeground() {
return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
mPaddingLeft + mForegroundPaddingLeft;
}
}
// ============== android.view.ViewGroup ==============
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//根据父view的MeasureSpec和自身的LayoutParams确定子View的MeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin +
lp.rightMargin + widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin +
lp.bottomMargin + heightUsed, lp.height);
//执行子View的测量流程
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
直到最内层的View
执行自身的onMeasure
。
View
的子类实现各有不同,默认实现为通过getDefaultSize
获取默认宽高。
getDefaultSize
会通过父view传递给view的MeasureSpec来计算最终宽高。
// ========== android.view.View ==============
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//默认的实现,不同子类有不同实现
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
//获取建议的最小高度
protected int getSuggestedMinimumHeight() {
//由layout:minHeight和background的最小高度决定
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
//
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
//标志位,表明已经进行了该View(ViewGroup)测量操作
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
//获取默认的尺寸,由父View传递的measureSpec和建议最小尺寸决定
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
2 layout
然后是对视图树内的所有View进行排版布局,执行performLayout
方法。
// ==============android.view.ViewRootImpl================
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
final View host = mView;
...
//最底层的DecorView
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
}
内部自然是执行DecorView
的layout
方法,确认ViewGroup内部所有子View的位置信息。
对于layout的参数事先说明一下(万恶的单字母缩写)
l
:自身View的左边缘与父容器左边缘的距离(left)
t
:自身View的上边缘与父容器上边缘的距离 (top)
r
:自身View的右边缘与父容器左边缘的距离 (right)
b
:自身View的下边缘与父容器上边缘的距离 (bottom)
// ========== android.view.ViewGroup ==============
@Override
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
//未执行动画时,直接调用父类View方法
if (mTransition != null) {
mTransition.layoutChange(this);
}
super.layout(l, t, r, b);
} else {
// record the fact that we noop'd it; request layout when transition finishes
mLayoutCalledWhileSuppressed = true;
}
}
// ========== android.view.View ================
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
//未执行过Measure,重新执行Measure流程
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
...
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
...
}
...
}
private boolean setOpticalFrame(int left, int top, int right, int bottom) {
Insets parentInsets = mParent instanceof View ?
((View) mParent).getOpticalInsets() : Insets.NONE;
Insets childInsets = getOpticalInsets();
return setFrame(
left + parentInsets.left - childInsets.left,
top + parentInsets.top - childInsets.top,
right + parentInsets.left + childInsets.right,
bottom + parentInsets.top + childInsets.bottom);
}
//确定View四个顶点的位置
//(初始化 mLeft、mTop、mRight、mBottom)
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
f (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged); //看着挺眼熟的哈
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
...
}
return changed
}
在layout
方法中,通过setFrame
方法确定了View四个顶点的位置,而顶点确立之后,其在父容器内的位置也就确定了。
当顶点位置发生变化或者添加了PFLAG_LAYOUT_REQUIRED
标志位后,会调用onLayout
方法。
View
和ViewGroup
内都是空实现,在不同子类中的实现则各不相同。这里就还是以
DecorView
所在的FrameLayout
为例来分析
// ========== android.widget.FrameLayout ================
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
...
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
//通过Gravity属性确定子View的顶点位置
...
//执行子View的布局操作,确定四个顶点位置。
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
3 draw
确定了视图的位置和大小,接下来终于到执行绘制视图的地方——performDraw
。
// ============== android.view.ViewRootImpl ================
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
private void performDraw() {
...
final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw;
...
boolean canUseAsync = draw(fullRedrawNeeded);
...
}
private boolean draw(boolean fullRedrawNeeded) {
//这里也就拿到了正确指向native对象引用的Surface
Surface surface = mSurface;
//没有可用的surface缓冲区域(Layer)是不执行draw操作的
if (!surface.isValid()) {
return false;
}
...
final Rect dirty = mDirty;
...
boolean useAsyncReport = false;
//绘制区域不为空,或者正在执行动画
if (!dirty.isEmpty() || mIsAnimating ...) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
//开启了硬件加速
...
//通过RenderThread的GPU渲染进行绘制
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
useAsyncReport = true;
}else{
//使用软件加速
...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
...
return useAsyncReport;
}
而draw
方法内部也分为了软件绘制和硬件绘制两种情况。
个人能力有限,这里暂且只关注软件绘制的情况,基本原理是差不多的。
关于硬件绘制
RenderThread
参见:
// ============== android.view.ViewRootImpl ================
/**
* @return true if drawing was successful, false if an error occurred
*/
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
// Draw with software renderer.
final Canvas canvas;
...
//1.申请buffer缓冲区,创建Canvas
canvas = mSurface.lockCanvas(dirty);
...
try {
...
//2.执行View的绘制操作,内部调用onDraw ,将内容写入buffer缓冲区
mView.draw(canvas);
...
}finally{
// 3.释放Canvas锁定的buffer缓冲区,交由SurfaceFlinger消费,显示缓冲区内容
surface.unlockCanvasAndPost(canvas);
...
}
...
return true
}
3.1 lockCanvas请求GraphicBuffer
既然View.draw
方法的Canvas
是由Surface
创建的,我们就先来看看lockCanvas
方法。
// =============== android.view.Surface =================
private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
throws OutOfResourcesException;
public Canvas lockCanvas(Rect inOutDirty)
throws Surface.OutOfResourcesException, IllegalArgumentException {
synchronized (mLock) {
//校验是否正确关联native的引用地址 mNativeObject != 0
checkNotReleasedLocked();
....
//调用native方法申请缓冲区域
mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
return mCanvas;
}
}
以下native方法均通过Android Search Code查看,需要翻墙。
代码基于Android11-rc3,限于篇幅有限,只保留关键核心代码。
// ============== frameworks/base/core/jni/android_view_Surface.cpp ==================
static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
//1.获取java层Surface保存的 native层Surface对象引用。
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
...
ANativeWindow_Buffer buffer;
//1.申请图像缓冲区并赋值给buffer
status_t err = surface->lock(&buffer, dirtyRectPtr);
...
//2.创建native的Canvas
graphics::Canvas canvas(env, canvasObj);
canvas.setBuffer(&buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
...
// 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();
}
nativeLockCanvas
方法内部大致分为三个步骤
- 获取
2.4
时创建的native层Surface
对象,调用其lock
方法获取到buffer图像缓冲区对象引用,赋值给buffer
变量。- 创建native层的Canvas,并设置buffer图层缓冲区,赋值给java层的Canvas对象引用
- 返回新创建的surface引用,表示已关联了native层的
Canvas
和Buffer
。
我们就只关注其中的Surface.lock
方法,看看究竟是如何获取到Buffer对象的。
// ============= frameworks/base/core/jni/android_view_Surface.cpp =================
status_t Surface::lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds){
if (!mConnectedToCpu) {
//如果没有连接上Cpu,则调用connect进行连接
int err = Surface::connect(NATIVE_WINDOW_API_CPU);
if (err) {
return err;
}
// we're intending to do software rendering from this point
setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
}
ANativeWindowBuffer* out;
int fenceFd = -1;
//1. 请求获取一个GraphicBuffer图形缓冲区,并赋值给out
status_t err = dequeueBuffer(&out, &fenceFd);
...
if (err == NO_ERROR) {
//2. 将获取的GraphicBuffer对象赋值给backBuff,标识为后备缓冲区
sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
...
void* vaddr;
//锁定取出的这个buffer,避免出现访问冲突
//将该buffer映射到当前进程的虚拟地址空间中,vaddr就是buffer起始地址。
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 {
//赋值给表示已锁定的Buff对象,指向当前申请到图形缓冲区
mLockedBuffer = backBuffer;
//3.将申请到的图形缓冲区,赋值给外部的对象引用。
outBuffer->width = backBuffer->width;
outBuffer->height = backBuffer->height;
outBuffer->stride = backBuffer->stride;
outBuffer->format = backBuffer->format;
outBuffer->bits = vaddr;
}
}
return err;
}
同样的, 我们重点关注Surface.dequeueBuffer
方法。
// ============= frameworks/base/core/jni/android_view_Surface.cpp ==============
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
...
int buf = -1;
...
// Binder跨进程调用,生产者向Buffer队列中申请可用的GraphicBuffer图形缓冲区,并将buffer对象所在的队列索引赋值buf
// Producer位于surfaceFlinger进程
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
reqFormat, reqUsage, &mBufferAge,
enableFrameTimestamps ? &frameTimestamps: nullptr);
...
//Surface本地映射的GraphicBuffer数组中指定索引的对象引用指针
//Surface和Producer并不在一个进程内,需要内存映射Buffer
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
...
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
// 如果没有在buffer队列内获取到对应buffer,
// 或者生产者对象的标志位表示需要重新分配buffer
if (mReportRemovedBuffers && (gbuf != nullptr)) {
mRemovedBuffers.push_back(gbuf);
}
// Producer获取指定索引下的buffer,并将buffer对象引用指针赋值给gbuf
// 通过Binder机制调用,将Producer所在进程的buffer对象映射填充到Surface的buffer数组内的指定索引内。
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
...
//出现错误要取消并释放buffer
mGraphicBufferProducer->cancelBuffer(buf, fence);
return result;
}
}
...
//将映射到本地的buffer对象引用地址,赋值给外部变量
*buffer = gbuf.get();
...
return OK;
}
// ============ frameworks/native/libs/gui/include/gui/Surface.h ===================
class Surface
: public ANativeObjectBase<ANativeWindow, Surface, RefBase>
{
protected:
//BufferSlot对应一个GraphicBuffer
struct BufferSlot {
sp<GraphicBuffer> buffer;
Region dirtyRegion;
};
// mSurfaceTexture is the interface to the surface texture server. All
// operations on the surface texture client ultimately translate into
// interactions with the server using this interface.
// TODO: rename to mBufferProducer
//实际就是构造native Surface时传入的生产者对象
sp<IGraphicBufferProducer> mGraphicBufferProducer;
// 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.
// GraphicBuffer的队列数组,最大64长度的数组 ,
// 存放SurfaceFlinger进程的Buffer队列的共享内存映射
BufferSlot mSlots[NUM_BUFFER_SLOTS];
...
}
这里就用到了在2.4
中创建的native层Surface
时,传入的IGraphicBufferProducer
对象了,而前面2.2
时分析的BufferQueueProducer
则是IGraphicBufferProducer
的Binder代理实现类。
关于
BufferQueueProducer
请求buffer的dequeueBuffer
和requestBuffer
,个人能力有限,这里就暂时不展开分析了。参见:
其中的流程图能清晰的描述这一过程
更多关于
BufferQueue
:
- 小结:
- 通过共享内存的方式,将
SurfaceFlinger
进程内的BufferQueue
队列中获取到的可用buffer对象,映射到当前App进程内,同时锁定该buffer的内存区域,避免访问冲突。 - 将该buffer对象赋值给新创建的native层的
Canvas
对象,并进而将native层Canvas
对象赋值给外部Java层Canvas
3.2 View.draw
前面已经通过拿到了GraphicBuffer
的映射,并赋值给创建的Canvas
对象。接下来也就是熟悉的用Canvas
进行绘制的操作。
官方的注释还是很详细的。
// ========== android.view.View ================
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
...
// Step 1, draw the background, if needed
drawBackground(canvas);
...
// Step 3, draw the content,绘制自身
onDraw(canvas);
...
// Step 4, draw the children,递归分发给内部子View,只在ViewGroup内有实现
dispatchDraw(canvas);
...
}
主要就是通过熟悉的Canvas
的drawXXX
系列方法,在内部持有的GraphicBuffer
缓冲区内写入显示的内容。
3.3 unlockCanvasAndPost
既然有申请Buffer写入,那写完之后自然要释放Buffer读取显示到设备。
// ============== android.view.ViewRootImpl ================
public void unlockCanvasAndPost(Canvas canvas) {
synchronized (mLock) {
//校验是否正确关联native的surface引用地址 mNativeObject != 0
checkNotReleasedLocked();
if (mHwuiContext != null) {
//释放硬件加速绘制的锁定
mHwuiContext.unlockAndPost(canvas);
} else {
//是否软件绘制的buffer
unlockSwCanvasAndPost(canvas);
}
}
}
private void unlockSwCanvasAndPost(Canvas canvas) {
if (canvas != mCanvas) {
throw new IllegalArgumentException("canvas object must be the same instance that "
+ "was previously returned by lockCanvas");
}
if (mNativeObject != mLockedObject) {
Log.w(TAG, "WARNING: Surface's mNativeObject (0x" +
Long.toHexString(mNativeObject) + ") != mLockedObject (0x" +
Long.toHexString(mLockedObject) +")");
}
if (mLockedObject == 0) {
throw new IllegalStateException("Surface was not locked");
}
try {
nativeUnlockCanvasAndPost(mLockedObject, canvas);
} finally {
nativeRelease(mLockedObject);
mLockedObject = 0;
}
}
//释放Canvas持有的buffer
private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);
//释放当前锁定Surface的引用
private static native void nativeRelease(long nativeObject);
调用native方法,最终通过Surface.unlockAndPost
方法将buffer对象解除锁定状态,重新放入BufferQueue
队列,交由SurfaceFlinger
消费合成显示到设备。
// ======== frameworks/base/core/jni/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
graphics::Canvas canvas(env, canvasObj); //转换成native的Canvas
canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN); //解除Canvas与Buffer的关联
// unlock surface
status_t err = surface->unlockAndPost();
...
}
static void nativeRelease(JNIEnv* env, jclass clazz, jlong nativeObject) {
sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
sur->decStrong(&sRefBaseOwner);
}
// ============ frameworks/native/libs/gui/Surface.cpp ==================
status_t Surface::unlockAndPost()
{
if (mLockedBuffer == nullptr) {
...
return INVALID_OPERATION;
}
int fd = -1;
//解锁surface映射的buffer缓冲区
status_t err = mLockedBuffer->unlockAsync(&fd);
...
// 将buffer重新放入BufferQueue队列内,通知SurfaceFlinger消费该buffer
err = queueBuffer(mLockedBuffer.get(), fd);
...
//二级缓冲机制,记录当前使用的buffer
mPostedBuffer = mLockedBuffer;
//将表示当前锁定buffer对象置空
mLockedBuffer = nullptr;
return err;
}
3.4 小结
至此,一帧的视图绘制就结束了。
实际上,在60帧的设备中,Vsync同步信号每16.6ms就会发送一次,所以特别是在onDraw方法中,如果一次(1帧)需要写入buffer的数据太多,就会导致该次
SurfaceFlinger
无法获取到该buffer数据,还是使用上一次的buffer进行绘制,进而导致丢帧卡顿。
总结
最后简要概括一下View绘制流程
依次执行
measure
、layout
操作,确定所有View的尺寸和在窗口中的位置。通过native层
Surface
内部持有Producer
的Binder代理对象,跨进程调用BufferQueueProducer.dequeueBuffer
方法向BufferQueue
队列申请可用的Buffer
。通过共享内存映射的方式,获取映射到App进程中的
Buffer
对象,同时锁定Buffer
避免访问冲突。创建
Canvas
对象,持有映射到Surface
虚拟内存地址的Buffer
对象,执行draw
操作,写入填充显示内容到Buffer
对象。
Buffer
写入结束后,释放Canvas关联,解除锁定Buffer
对象。通过queueBuffer
方法,将Buffer
对象接触重新放入队列,通知SurfaceFlinger
消费显示到设备。
后记
通过三篇文章粗略的梳理了一次绘制流程,实际的绘制流程其实相当复杂,涉及到很多东西,远不是这三篇粗浅文章能够讲清楚的。
目前主要还是先对应用层上的绘制流程有一个初步的,大体上的认识,为后续深入分析其中细节做好铺垫。
参考资料
Android Java层UI渲染实现 四 Surface的创建和View绘制