Android绘制流程 —— View绘制

1,399 阅读13分钟

前言

本篇是Android绘制流程的第三篇,在Window连接Surface创建中介绍了应用是如何与SurfaceFlinger关联,并且创建了可以绘制的图层。

接下来就接着向下分析,看看视图是如何绘制是如何在开辟出来的Surface上绘制的。

作为复习记录总结,同样只是进行一个粗略的流程上的探究。

这里暂且先忽略一些难懂的细节,以建立起一个大致的概念为目标。

如果有误,欢迎指出,共同进步

源码解析

既然已经有了可以绘制的图层,接下来自然就到熟悉的视图绘制三剑客——measurelayoutdraw

以下代码基于 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 父容器指定了一个可用大小即SpecSizeView的大小不能大于这个值,一般在设置了wrap_content 时设置了。

对于最外层DecorViewMeasureSpec,则是默认通过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());
    ...
}

内部自然是执行DecorViewlayout方法,确认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方法。

ViewViewGroup内都是空实现,在不同子类中的实现则各不相同。

这里就还是以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-Surface之创建流程及软硬件绘制

Android Systrace 基础知识(5) - SurfaceFlinger 解读

Android Systrace 基础知识(9)-MainThread 和 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方法内部大致分为三个步骤

  1. 获取2.4时创建的native层Surface对象,调用其lock方法获取到buffer图像缓冲区对象引用,赋值给buffer变量。
  2. 创建native层的Canvas,并设置buffer图层缓冲区,赋值给java层的Canvas对象引用
  3. 返回新创建的surface引用,表示已关联了native层的CanvasBuffer

我们就只关注其中的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的dequeueBufferrequestBuffer,个人能力有限,这里就暂时不展开分析了。

参见:

surface申请GraphicBuffer过程

图形缓冲区的申请和消费流程及核心类

其中的流程图能清晰的描述这一过程

dequeueBuffer流程

img

更多关于BufferQueue

深入浅出Android BufferQueue

graphics 学习-生产者、消费者、BufferQueue介绍

Android Systrace 基础知识 - Triple Buffer 解读

  • 小结:
  1. 通过共享内存的方式,将SurfaceFlinger进程内的BufferQueue队列中获取到的可用buffer对象,映射到当前App进程内,同时锁定该buffer的内存区域,避免访问冲突。
  2. 将该buffer对象赋值给新创建的native层的Canvas对象,并进而将native层Canvas对象赋值给外部Java层Canvas

关于匿名共享内存原理

Android 匿名共享内存的使用

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);    
    ... 
}

主要就是通过熟悉的CanvasdrawXXX系列方法,在内部持有的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进行绘制,进而导致丢帧卡顿。

关于双缓冲与三缓冲机制

APP 卡顿问题分析及解决方案

img

总结

最后简要概括一下View绘制流程

  1. 依次执行measurelayout操作,确定所有View的尺寸和在窗口中的位置。

  2. 通过native层Surface内部持有Producer的Binder代理对象,跨进程调用BufferQueueProducer.dequeueBuffer方法向BufferQueue队列申请可用的Buffer

    通过共享内存映射的方式,获取映射到App进程中的Buffer对象,同时锁定Buffer避免访问冲突。

  3. 创建Canvas对象,持有映射到Surface虚拟内存地址的Buffer对象,执行draw操作,写入填充显示内容到Buffer对象。

  4. Buffer写入结束后,释放Canvas关联,解除锁定Buffer对象。通过queueBuffer方法,将Buffer对象接触重新放入队列,通知SurfaceFlinger消费显示到设备。

后记

通过三篇文章粗略的梳理了一次绘制流程,实际的绘制流程其实相当复杂,涉及到很多东西,远不是这三篇粗浅文章能够讲清楚的。

目前主要还是先对应用层上的绘制流程有一个初步的,大体上的认识,为后续深入分析其中细节做好铺垫。

参考资料

Android Java层UI渲染实现 四 Surface的创建和View绘制

聊聊Surface跨进程传递

Android 图形架构 之六——深入分析draw()是如何工作的

Android View事件分发、绘制流程

surface申请GraphicBuffer过程