前言
本篇是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绘制