Android显示系统的基本原理(一)

前言

对于操作系统而言,图形显示部分是非常重要的一个模块,用来承载用户交互与内容展示,因此本文其实无法对Android的显示系统做非常详尽的分析,因为如果那样的话可能需要一个系列,而且对于一般开发者而言并没有这样的必要,因此本文主要从View的显示作为切入点来展开显示系统的基本原理以及它的运行机制,力求深入浅出,帮助大家对它有比较深入的理解。

本文分析依赖的Android源码版本主要是Android13(或以上)

关于View的显示

  • 对于Android开发者而言,大家都接触过Android的View的开发,那么一个View是如何绘制并显示到设备的屏幕上呢?

    • 当然是ondraw方法中使用canvas进行绘制的,然后把绘制出的像素数据传递到显示系统中
  • 那么canvas是什么?从哪里来的呢?

    • canvas我们称之为画布,通过canvas进行绘制操作之后,最终绘制的像素数据会渲染到屏幕上;而canvas则是通过Surface调用lockCanvas(rect)锁住绘制区域之后获得的。在我们绘制完成之后会释放掉。
  • 那么什么是Surface?

    • 现在,我们可以说进入正题了,能够理解Surface,对于我们理解Android呃显示系统非常有帮助,因为它是显示系统中的一个关键部分,而且是开发者可能接触到的那个部分。

    • Surface称作表面,但实际是汇集该window所产生的渲染数据,然后统一传递到显示模块中。stackoverflow中有一个关于surface,canvas的概念解释对surface的描述也很贴切

Android显示系统

我们先对Android显示系统做一个结构概括:那就是显示系统是一个多对一的生产消费模型架构的系统,数据生产端是View,camera,Opengl ES等。消费端一般是由surfaceflinger掌控并负责把渲染数据合成为一个帧,并发送到显示控制器(屏幕)。

或许这张图更加纯粹

生产者一般包括:

  • Android View,通过canvas绘制产生图像
  • media player 解码视频数据播放
  • Camera 摄像头产生图像
  • Opengl ES

而消费者主要是SurfaceFlinger。

生产者和消费者之间存在一个缓冲队列,用于发送数据和消费数据。但是对于生产者而言,其实并不直接与缓冲队列接触,而是通过Surface,所有的生产者都是通过Surface提供的接口输入渲染数据。或许我们可以简单的理解,对于数据生产方而言,Surface是渲染数据的汇集地。

接下来我们以Android view的绘制体系为例来看看显示系统中渲染数据是如何产生并且输入到缓冲队列中的。

渲染数据如何产生

关于Android View的绘制体系,其实指的就是在View Tree中通过onDraw回调,使用canvas逐个进行绘制的体系。

typescript 复制代码
@Override
protected void onDraw(Canvas canvas) {
    
    // Draw the background for this view
    super.onDraw(canvas);
    ...
}

因为前文已经问到了canvas的问题,确定是由Surface产生的,那么接下来问题就转移到Surface身上了,想要弄清楚渲染数据如何产生,就要先弄懂Surface的创建和使用细节,我们从代码层面来解释下为什么说Surface是一个重要的结构。

surface的创建过程

上层视角

与Surface伴随的类叫SurfaceController,这是要对Surface进行管理的一个类,我们能够在ViewRootImpl中看到这两个类的对象。而Surface的创建依赖于SurfaceController的创建,因此我们不得不先分析SurfaceController的创建过程。

先依次查看Surface和SurfaceController的类的主要结构

scss 复制代码
public class Surface  implements Parcelable{

...
// 指向cpp层的surface对象
long mNativeObject; // package scope only for SurfaceControl access
...


public void createFrom(SurfaceControl other) {
    ...
    long surfaceControlPtr = other.mNativeObject;
    ...
    long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
    ...
    synchronized (mLock) {
        if (mNativeObject != 0) {
            nativeRelease(mNativeObject);
        }
        setNativeObjectLocked(newNativeObject);
    }
}

// 通过SurfaceControl复制数据
public void copyFrom(SurfaceControl other) {
    ...
    long surfaceControlPtr = other.mNativeObject;
    ...
    long newNativeObject = nativeGetFromSurfaceControl(mNativeObject, surfaceControlPtr);
    updateNativeObject(newNativeObject);
}

}

Surface在Java层的结构中有一个mNativeObject属性,顾名思义就是指cpp层的surface对象,SurfaceController也是类似的情况。

ini 复制代码
// Handle to an on-screen Surface managed by the system compositor
// 主要用于管理Surface,命名方式也能看出
public final class SurfaceControl implements Parcelable {

public long mNativeObject; // 也有一个cpp层对应的类

// 能够同时创建native层的对象的构造函数
private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
        SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView,
        String callsite)
                throws OutOfResourcesException, IllegalArgumentException {
     ...
    mName = name;
    mWidth = w;
    mHeight = h;
    mLocalOwnerView = localOwnerView;
    Parcel metaParcel = Parcel.obtain();
    try {
        ...
        // 创建cpp层的SurfaceControl
        mNativeObject = nativeCreate(session, name, w, h, format, flags,
                parent != null ? parent.mNativeObject : 0, metaParcel);
    } finally {
        metaParcel.recycle();
    }
    ...
    mNativeHandle = nativeGetHandle(mNativeObject);
    mCloseGuard.openWithCallSite("release", callsite);
}


/**
 * 复制SurfaceControl
 */
public void copyFrom(@NonNull SurfaceControl other, String callsite) {
    mName = other.mName;
    mWidth = other.mWidth;
    mHeight = other.mHeight;
    mLocalOwnerView = other.mLocalOwnerView;
    assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite);
}

}

我们可以在ViewRootImpl中找到mSurface,mSurfaceControl,它们都是是使用默认构造方法创建的对象,像这样涉及底层能力的类,一般是有一个cpp层的对象来实现真实的功能和交互的,因此实际上默认构造出来的对象还只是一个壳子,因此我们需要看它的cpp层是何时被创建出来的。

SurfaceControl的创建

java 复制代码
// viewrootimpl.java


public final Surface mSurface = new Surface();
private final SurfaceControl mSurfaceControl = new SurfaceControl();

private void performTraversals() {
    ...
    boolean hadSurface = mSurface.isValid();
    ...
    // relayoutWindow最终通过IWindowSession接口走了一个跨进程调用到WMS中
    relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
    ...
    surfaceCreated = !hadSurface && mSurface.isValid();
    ...
}


private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {

    ...
    //跨进程调用,传入mSurfaceControl
    relayoutResult = mWindowSession.relayout(mWindow, params,
            requestedWidth, requestedHeight, viewVisibility,..., mSurfaceControl,...);
    ...
    // 判断mSurfaceControl是否合法
    if (mSurfaceControl.isValid()) {
        if (!useBLAST()) {
            mSurface.copyFrom(mSurfaceControl);
        } else {
            updateBlastSurfaceIfNeeded();
        }
    ...
    ...
    }
}

relayoutWindow最终触发了跨进程调用到WMS.relayoutWindow中。然后进一步调用了createSurfaceControl方法,创建了一个SurfaceControl

csharp 复制代码
// WindowManagerService.java
// outSurfaceControl 就是ViewRootImpl中传入的SurfaceControl空壳对象
private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
        WindowState win, WindowStateAnimator winAnimator) {
    ...
    WindowSurfaceController surfaceController;
    ...
        //1, 创建一个WindowSurfaceController
        surfaceController = winAnimator.createSurfaceLocked();
    ...
    if (surfaceController != null) {
         //2, 新创建的surfaceController复制到外部传入的outSurfaceController
        surfaceController.getSurfaceControl(outSurfaceControl);
    }
    ...
    return result;
}

/*************************************************/
// WindowStateAnimator.java
WindowSurfaceController createSurfaceLocked() {
    ...
    // 创建一个SurfaceController,使用的是前面介绍的能够创建native对象的构造函数
    mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
            flags, this, attrs.type);

    w.setHasSurface(true); // 此时WindowStateAnimator中就有了surfaceControl(Java层和cpp层都有)
    ...
}

总代码上看,实现逻辑是:WindowStateAnimator先通过构造方法创建一个的SurfaceControl对象,然后通过复制,把该对象复制到outSurfaceControl中(也就是ViewRootImpl中mSurfaceControl)。

我们先看创建SurfaceControl的构造函数所调用的native函数的详情

arduino 复制代码
//frameworks/base/core/jni/android_view_SurfaceControl.cpp

static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
        jobject metadataParcel) {
    ScopedUtfChars name(env, nameStr);
    sp<SurfaceComposerClient> client;
    if (sessionObj != NULL) {
    //获取SurfaceSession(SurfaceFLinger的连接类,在native层的对应类就是SurfaceComposerClient)
        client = android_view_SurfaceSession_getClient(env, sessionObj);
    } else {
        client = SurfaceComposerClient::getDefault();
    }
    ...
    sp<SurfaceControl> surface;
    ...
    // 创建surfacecontrol
    status_t err = client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface,flags, parentHandle, std::move(metadata));
    ...
    return reinterpret_cast<jlong>(surface.get());
}

// frameworks/native/libs/gui/SurfaceComposerClient.cpp
status_t SurfaceComposerClient::createSurfaceChecked(...) {
  status_t err = mStatus;
  // 调用到远端SurfaceFlinger,同步创建一个Surface
   binder::Status status = mClient->createSurface(std::string(name.c_str()), flags,parentHandle,std::move(metadata),&result);
    if (mStatus == NO_ERROR) {
        ...
        if (err == NO_ERROR) {
            // new一个cpp层的SurfaceControl
            *outSurface = new SurfaceControl(this, result.handle, result.layerId,
                                             toString(result.layerName), w, h, format,
                                             result.transformHint, flags);
        }
    }
    return err;
}

//frameworks/native/services/surfaceflinger/Client.cpp
// 调用到surfaceflinger,创建surface
binder::Status Client::createSurface(const std::string& name, int32_t flags,
                                     const sp<IBinder>& parent, const gui::LayerMetadata& metadata,
                                     gui::CreateSurfaceResult* outResult) {
    // We rely on createLayer to check permissions.
    sp<IBinder> handle;
    LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), name.c_str(),
                           static_cast<uint32_t>(flags), std::move(metadata));
    args.parentHandle = parent;
    // flinger创建layer(可以视作Surface)
    const status_t status = mFlinger->createLayer(args, *outResult);
    return binderStatusFromStatusT(status);
}

native层的实现逻辑主要是:通过SurfaceComposerClient这个surfaceflinger连接类,先在远端的surfaceflinger创建一个Surface(实际是Layer),然后再创建一个SurfaceControl对象返回,此时 SurfaceControl是一个完整对象(既有Java层对象也有cpp层对象)。

接着回到Java层,查看第二步getSurfaceControl的复制逻辑:

arduino 复制代码
// 复制给ViewRootImpl中的mSurfaceControl
// WindowSurfaceController.java  
void getSurfaceControl(SurfaceControl outSurfaceControl) {
    // 最终从Java层的copyFrom调用到 cpp层的nativeCopyFromSurfaceControl
   outSurfaceControl.copyFrom(mSurfaceControl,"WindowSurfaceController.getSurfaceControl");
}

/*************************************************/

//frameworks/base/core/jni/android_view_SurfaceControl.cpp

static jlong nativeCopyFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) {
    sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
    if (surface == nullptr) {
        return 0;
    }
    // 利用传入的cpp层的surfacecontrol对象创建新的SurfaceControl
    sp<SurfaceControl> newSurface = new SurfaceControl(surface);
    // 调用nativeCreate方法
    newSurface->incStrong((void *)nativeCreate);
    return reinterpret_cast<jlong>(newSurface.get());
}

创建了SurfaceControl之后,接着调用getSurfaceControl进行复制,复制时会调用nativeCopyFromSurfaceControl这个native方法, 把cpp层的SurfaceControl也一并复制过来。

WindowStateAnimator中创建了一个完整的SurfaceControl,然后也把自己复制给ViewRootImpl的SurfaceControl中(无论是Java层还是native层),至此SurfaceControl的创建过程结束了。

Surface的创建过程

接下来回到ViewRootImpl中,后面的代码执行如下:

scss 复制代码
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {

    ...
    //跨进程调用,传入mSurfaceControl
    relayoutResult = mWindowSession.relayout(mWindow, params,
            requestedWidth, requestedHeight, viewVisibility,..., mSurfaceControl,...);
    ...
    // 判断mSurfaceControl是否合法
    if (mSurfaceControl.isValid()) {
        if (!useBLAST()) {
            mSurface.copyFrom(mSurfaceControl);
        } else {
            updateBlastSurfaceIfNeeded();
        }
    ...
    ...
    }
}

正常情况下,mSurfaceControl是合法的,接下来无论是useBLAST是否为真,都可以创建Surface。

第一种方法

第一种情况调用copyFrom:

java 复制代码
// Surface.java
@UnsupportedAppUsage
public void copyFrom(SurfaceControl other) {
    ...
    long surfaceControlPtr = other.mNativeObject;
    ...
    // 分别传入Surface的native对象句柄和SurfaceControl native对象句柄
    long newNativeObject = nativeGetFromSurfaceControl(mNativeObject, surfaceControlPtr);
    // 更新Java层的信息,mNativeObject的句柄
    updateNativeObject(newNativeObject);
}

复制方法比较简单,就是从SurfaceControl获取一个Surface,然后更新一下自身。我们重点看一下nativeGetFromSurfaceControl这个native方法的执行逻辑是怎样的。

arduino 复制代码
// frameworks/base/core/jni/android_view_Surface.cpp
static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz,
        jlong nativeObject,
        jlong surfaceControlNativeObj) {
    Surface* self(reinterpret_cast<Surface *>(nativeObject));
    sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
    ...
    ...
    sp<Surface> surface(ctrl->getSurface()); // 调用SurfaceControl.getSurface函数
    ...
    return reinterpret_cast<jlong>(surface.get());
}

/******************************文件分割线******************************************/

// frameworks/native/libs/gui/SurfaceControl.cpp
sp<Surface> SurfaceControl::getSurface()
{
    Mutex::Autolock _l(mLock);
    // 如果是第一次进入,则需要创建Surface,然后被保存在SurfaceControl中
    if (mSurfaceData == nullptr) {
        return generateSurfaceLocked(); // 生成Surface
    }
    // 后面再获取则直接返回
    return mSurfaceData;
}

sp<Surface> SurfaceControl::generateSurfaceLocked()
{
    uint32_t ignore;
    auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow |
                                 ISurfaceComposerClient::eOpaque);
    mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat,
                                       flags, mHandle, {}, &ignore);
    //1,创建BLASTBufferQueue,内部同步会创建IGraphicBufferProducer和 IGraphicBufferConsumer
    mBbq = sp<BLASTBufferQueue>::make("bbq-adapter", mBbqChild, mWidth, mHeight, mFormat);


    // 2,调用BLASTBufferQueue->getSurface(...)  获取SUrface
    mSurfaceData = mBbq->getSurface(true);

    return mSurfaceData;
}
/******************************文件分割线******************************************/
// frameworks/native/libs/gui/BLASTBufferQueue.cpp
sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) {
    std::lock_guard _lock{mMutex};
    sp<IBinder> scHandle = nullptr;
    if (includeSurfaceControlHandle && mSurfaceControl) {
    // 获得一个Handle,可与SurfaceFlinger通信
        scHandle = mSurfaceControl->getHandle();
    }
    
    //sp<IGraphicBufferProducer> mProducer 是BLASTBufferQueue内部的一个属性,用来操作生产者数据入队出队
    return new BBQSurface(mProducer, true, scHandle, this);
}

我们发现创建cpp层面的Surface之前,会先创建一个队列BLASTBufferQueue,而队列内部有生产者操作类(IGraphicBufferProducer)和消费者操作类BufferQueueConsumer,正应了我们说到的生产消费模型的说法。

创建BBQSurface时,我们发现IGraphicBufferProducer类型的producer作为构造参数被传入了,这也印证了我们说的,Surface是生产者渲染数据汇集地。

第二种方法

markdown 复制代码
/******************************文件分割线******************************************/
// ViewRootImpl.java
void updateBlastSurfaceIfNeeded() {
    //此时mSurfaceControl是合法的
    if (!mSurfaceControl.isValid()) {
        return;
    }
    //第一次进入时mBlastBufferQueue应该是null
    if (mBlastBufferQueue != null && mBlastBufferQueue.isSameSurfaceControl(mSurfaceControl)) {
        mBlastBufferQueue.update(mSurfaceControl,
            mSurfaceSize.x, mSurfaceSize.y,
            mWindowAttributes.format);
        return;
    }
   ...
   ...
   //创建BLASTBufferQueue,这个显然就是第一种创建方法中native层的BLASTBufferQueue的Java层对应类
    // 构造方法最终会创建native层的BLASTBufferQueue
    mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
            mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
    mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
    // 通过BlastBufferQueue创建Surface
    Surface blastSurface = mBlastBufferQueue.createSurface();
    // 更新到surface内部
    mSurface.transferFrom(blastSurface);
}

/******************************文件分割线******************************************/
// frameworks/base/graphics/java/android/graphics/BLASTBufferQueue.java
public final class BLASTBufferQueue {
    // Note: This field is accessed by native code.
    public long mNativeObject; // BLASTBufferQueue native层有对应的对象

    public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
            @PixelFormat.Format int format) {
        this(name, true /* updateDestinationFrame */);
        update(sc, width, height, format);
    }

    public BLASTBufferQueue(String name, boolean updateDestinationFrame) {
        // 创建cpp层对应的BLASTBufferQueue
        mNativeObject = nativeCreate(name, updateDestinationFrame);
    }
    ...
    ...
    // nativeGetSurface最终调用到native层的
    public Surface createSurface() {
        return nativeGetSurface(mNativeObject, false /* includeSurfaceControlHandle */);
    }
}

/******************************文件分割线******************************************/
//frameworks/base/core/jni/android_graphics_BLASTBufferQueue.cpp

static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,
                                jboolean includeSurfaceControlHandle) {
    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
    
    // 最终还是调用了BLASTBufferQueue->getSurface(...)
   return android_view_Surface_createFromSurface(env, queue->getSurface(includeSurfaceControlHandle));
   
}

从上面的截取的代码可以看到即使是先创建Java层的BlastBufferQueue,最终也还是殊途同归,通过native层的BlastBufferQueue来创建一个Surface。

默认情况下,代码应该是第二种方法创建Surface。

创建阶段出现大量的类,我们用一个示意图来总结一下他们之间的关系:

android View的绘制过程

分析完了Surface的创建过程之后,我们再次回到View的绘制过程,从View的显示过程中理解Surface的作用。

我们都知道View的绘制需要canvas,而canvas则是通过ViewRootImpl内部的Surface来获取。

scss 复制代码
//ViewRootImpl.java
// 假设使用cpu绘制而不是硬件加速
private boolean drawSoftware(Surface surface,...) {      
    ...
    //获取canvas,dirty是绘制区域,一般而言需要更新的childview或者整块contentview显示区域
    canvas = mSurface.lockCanvas(dirty);
    ...
    mView.draw(canvas);//最终调用到onDraw方法
    ...
    // 释放canvas
    surface.unlockCanvasAndPost(canvas);
    ...
        
}

绘制过程可以简单描述为三步:

  • 通过Surface获得一个有效的Canvas
  • view通过canvas来进行绘制
  • Surface释放并发送canvas

我们可以按照这三部步骤来分析

获得有效的Canvas

我们可以先简单看一下Canvas类的情况

ini 复制代码
public class Canvas extends BaseCanvas {
    protected long mNativeCanvasWrapper;    
    public Canvas() {
            if (!isHardwareAccelerated()) { // 非硬件加速模式下创建mNativeCanvasWrapper
                // 0 means no native bitmap
                mNativeCanvasWrapper = nInitRaster(0);
                mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
                        this, mNativeCanvasWrapper);
            } else {
                mFinalizer = null;
            }
        }
        public Canvas(@NonNull Bitmap bitmap) {
            ...
            throwIfCannotDraw(bitmap);
            mNativeCanvasWrapper = nInitRaster(bitmap.getNativeInstance());
            mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
                    this, mNativeCanvasWrapper);
            mBitmap = bitmap;
            mDensity = bitmap.mDensity;
        }
        public Canvas(long nativeCanvas) {
            ...
            mNativeCanvasWrapper = nativeCanvas;
            mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
                    this, mNativeCanvasWrapper);
            mDensity = Bitmap.getDefaultDensity();
        }
}

canvas由于涉及到底层的图像绘制,那么和surface一样,在cpp层也有一个对应的类实现底层功能,这个类的句柄就是mNativeCanvasWrapper。

markdown 复制代码
/******************************文件分割线******************************************/
// Surface.java

private final Canvas mCanvas = new CompatibleCanvas();
public Canvas lockCanvas(Rect inOutDirty)
        throws Surface.OutOfResourcesException, IllegalArgumentException {
    synchronized (mLock) {
        ...
        mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
        return mCanvas;
    }
}

public void unlockCanvasAndPost(Canvas canvas) {
    synchronized (mLock) {
            ...
            unlockSwCanvasAndPost(canvas);

    }
}

我们可以观察到surface获取和释放canvas的方法都调用到了native层面,而canvas通过构造函数创建对象时也会创建一个cpp层的对象并获得句柄。我们看看canvas的构造

markdown 复制代码
//frameworks/base/libs/hwui/jni/android_graphics_Canvas.cpp
static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
    SkBitmap bitmap;
    if (bitmapHandle != 0) {
        bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap);
    }
    return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
}

/******************************文件分割线******************************************/
// frameworks/base/libs/hwui/SkiaCanvas.cpp
Canvas* Canvas::create_canvas(const SkBitmap& bitmap) {
    return new SkiaCanvas(bitmap);
}

最终在cpp层创建了SkiaCanvas。此时传入的SkBitmap引用是空的,没有任何有效信息。

我们接着往native层看lockCanvas的实现。

cpp 复制代码
static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
        // 获取对应native层的SUrface对象
    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 buffer;
    // 1,surface调用lock函数锁定一块buffer
    status_t err = surface->lock(&buffer, dirtyRectPtr);
    ...
    ...
    //2,通过Java层的canvas对象初始化一个native层的canvas对象
    graphics::Canvas canvas(env, canvasObj);
    //3,设置buffer,然后canvas把buffer转换为SKBitmap
    canvas.setBuffer(&buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
    ...
    // 创建一个新的Surface引用。返回到Java层
    sp<Surface> lockedSurface(surface);
    lockedSurface->incStrong(&sRefBaseOwner);
    return (jlong) lockedSurface.get();
}

/******************************文件分割线******************************************/
//frameworks/native/libs/nativewindow/include/android/native_window.h
// 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;

lockCanvas主要分为三步:1,申请并锁定一块内存,2,获取native层的canvas对象,3把buffer设置进canvas。

我们先来看第一步的细节:

ini 复制代码
//frameworks/native/libs/gui/Surface.cpp  ===> surface->lock(...)
status_t Surface::lock(
        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
    ANativeWindowBuffer* out;
    int fenceFd = -1;
    // dequeueBuffer 从输入缓冲队列中获取一块内存
    status_t err = dequeueBuffer(&out, &fenceFd);
    if (err == NO_ERROR) {
        //当前申请的图形内存区域作为backBuffer
        sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
        const Rect bounds(backBuffer->width, backBuffer->height);

        Region newDirtyRegion;
        if (inOutDirtyBounds) {
            newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
            newDirtyRegion.andSelf(bounds);
        } else {
            newDirtyRegion.set(bounds);
        }

        // figure out if we can copy the frontbuffer back
        // mPostedBuffer作为正在显示的一块图像区域
        const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
        //其实这里就涉及到SUrface的双缓冲机制 mPostedBuffer/mLockedBuffer两块buffer
        // 这里要判断是否可以复制(比较一下backBuffer与frontBuffer)
        const bool canCopyBack = (frontBuffer != nullptr &&
                backBuffer->width  == frontBuffer->width &&
                backBuffer->height == frontBuffer->height &&
                backBuffer->format == frontBuffer->format);

        if (canCopyBack) { // 可以赋值时
            //计算一下不需要重绘的区域
            const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
            if (!copyback.isEmpty()) {
                // 复制不需要重绘的区域
                copyBlt(backBuffer, frontBuffer, copyback, &fenceFd);
            }
        } else {
            // 设置脏区域的边界
            newDirtyRegion.set(bounds);
            //不能复制则清理一下原先的数据
            mDirtyRegion.clear();
            Mutex::Autolock lock(mMutex);
            for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
                mSlots[i].dirtyRegion.clear();
            }
        }
        ...

        // 锁定
        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 {
            //申请的这块backBuffer保存为mLockedBuffer
            mLockedBuffer = backBuffer;
            outBuffer->width  = backBuffer->width;
            outBuffer->height = backBuffer->height;
            outBuffer->stride = backBuffer->stride;
            outBuffer->format = backBuffer->format;
            outBuffer->bits   = vaddr;
        }
    }
    return err;
}

获取一块图形绘制缓冲区,包含绘制所需的一些信息。

第二步,获取native层的SkiaCanvas

markdown 复制代码
//frameworks/base/libs/hwui/apex/include/android/graphics/canvas.h
namespace graphics {
    class Canvas {
    public:
        Canvas(JNIEnv* env, jobject canvasObj) :
                        // 调用ACanvas_getNativeHandleFromJava
                mCanvas(ACanvas_getNativeHandleFromJava(env, canvasObj)),
                mOwnedPtr(false) {}


	}
}

/******************************文件分割线******************************************/
//frameworks/base/libs/hwui/apex/android_canvas.cpp
ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvasObj) {
                                //继续调用getNativeCanvas
    return TypeCast::toACanvas(GraphicsJNI::getNativeCanvas(env, canvasObj));
}


/******************************文件分割线******************************************/
//frameworks/base/libs/hwui/jni/Graphics.cpp
...
gCanvas_nativeInstanceID = GetFieldIDOrDie(env, gCanvas_class, "mNativeCanvasWrapper", "J");
...
android::Canvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
    ...
    // 获取Java层的Canvas mNativeCanvasWrapper的句柄
    jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
    if (!canvasHandle) {
        return NULL;
    }
    // 把数值转换为对象
    return reinterpret_cast<android::Canvas*>(canvasHandle);
}

通过从Java层传入的Canvas引用,获得native层创建好的SkiaCanvas。

然后是第三步

arduino 复制代码
frameworks/base/libs/hwui/apex/include/android/graphics/canvas.h
bool setBuffer(const ANativeWindow_Buffer* buffer,
                       int32_t /*android_dataspace_t*/ dataspace) {
            return ACanvas_setBuffer(mCanvas, buffer, dataspace);
 }
 
 
 /******************************文件分割线******************************************/
// frameworks/base/libs/hwui/apex/android_canvas.cpp
bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
                       int32_t /*android_dataspace_t*/ dataspace) {
    SkBitmap bitmap;
    // 把buffer转换为SKBitmap
    bool isValidBuffer = (buffer == nullptr) ? false : convert(buffer, dataspace, &bitmap);
    // 然后把SKBitmap设置进SkiaCanvas中(SKBitmap可以被canvas绘制)
    TypeCast::toCanvas(canvas)->setBitmap(bitmap);
    return isValidBuffer;
}

 /******************************文件分割线******************************************/
//frameworks/base/libs/hwui/SkiaCanvas.cpp   setBitmap的实现函数
void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {

    // 根据传入的bitmap创建一个SkCanvas,并更新
    mCanvasOwned.reset(new SkCanvas(bitmap));
    mCanvas = mCanvasOwned.get();

    // clean up the old save stack
    mSaveStack.reset(nullptr);
}

把获取的buffer转换为一个SkBitmap,此时bitmap中有有效信息,然后把Bitmap设置进SkiaCanvas并替换原来的Bitmap,接下来canvas就在这个bitmap上进行绘制。

setBuffer既用于设置buffer,也用于解除buffer,后面释放提交canvas时还会使用

通过Canvas进行绘制

绘制过程主要在发生View的体系中,根据View的需要自行进行绘制操作,Java层的部分应当是我们最熟悉的部分了。

当然所有的绘制API都通过JNI调用到了native层的SkiaCanvas中,然后绘制在SKBitmap上。这些都由skia图像绘制引擎提供具体实现逻辑。在此我们不进行深究了(精力有限)

释放并发送Canvas

假设绘制区域完成了绘制操作,那么接下来就是释放并提交canvas了。Java层通过nativeUnlockCanvasAndPost调用到native层,我们看其具体实现

arduino 复制代码
//frameworks/base/core/jni/android_view_Surface.cpp
static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj) {
        // 找到native层的Surface
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
    ...
    //找到native层的SkiaCanvas
    graphics::Canvas canvas(env, canvasObj);
    //SkiaCanvas设置当前buffer为空
    canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);

    // unlock surface
    status_t err = surface->unlockAndPost();
    ...
}

里面的函数前面大多都讲过, canvas.setBuffer(nullptr,..),nullptr会转换为一个空的skBitmap,然后替换SkiaCanvas里面原有的SkBitmap。相当于让canvas与原来的buffer分离。

ini 复制代码
// frameworks/native/libs/gui/Surface.cpp
status_t Surface::unlockAndPost()
{
  //surface->lock()时已经保存了一个mLockedBuffer,此时应该不为null
    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);
    //把这块绘制完毕的buffer提交到缓冲队列中,等待显示
    err = queueBuffer(mLockedBuffer.get(), fd);
    ...
    // 接着这块buffer就变成了前台已发布的buffer了,这个就是双缓冲机制了
    mPostedBuffer = mLockedBuffer;
    //置空
    mLockedBuffer = nullptr;
    return err;
}

至此,View的绘制的详细过程我们也搞清楚了:ViewRootImp通过Surface从缓冲队列中获得一块可用于绘制的buffer,然后把buffer绑定在canvas中,Android View使用该canvas进行绘制,产生的渲染数据也最终保存在buffer中,绘制完毕,通过Surface清除canvas与buffer的绑定关系,并把buffer发送到缓冲队列中,完成了生产端渲染数据的生产过程。

小结

在渲染数据生产过程中,Surface的低位非常重要,我们文章开头提到的那些生产者(view/Camera/media player...)都离不开它,Surface提供了操作接口来完成生产端的数据输入过程。在消费端, 大部分情况下是通过SurfaceFlinger把渲染数据的用在屏幕显示来完成消费的。但是也有其他情况,比如也可以被视频编码器进行消费(后续关于Android音视频开发的文章可能会提到)。

总之Surface给图形数据生产者提供了了一个数据输入的标准接口,我们会在大量的系统模块中看到它的身影。

文章到这里显示原理差不多只讲到一半,生产消费模型只讲到了生产端,消费端还没有涉及,还有缓冲队列的具体操作,这个留在后续文章进行研究。

相关推荐
海上彼尚2 分钟前
实现3D热力图
前端·javascript·3d
杨过姑父2 分钟前
org.springframework.context.support.ApplicationListenerDetector 详细介绍
java·前端·spring
理想不理想v12 分钟前
使用JS实现文件流转换excel?
java·前端·javascript·css·vue.js·spring·面试
惜.己32 分钟前
Jmeter中的配置原件(四)
java·前端·功能测试·jmeter·1024程序员节
EasyNTS33 分钟前
无插件H5播放器EasyPlayer.js网页web无插件播放器vue和react详细介绍
前端·javascript·vue.js
poloma38 分钟前
五千字长文搞清楚 Blob File ArrayBuffer TypedArray 到底是什么
前端·javascript·ecmascript 6
想取一个与众不同的名字好难1 小时前
android studio导入OpenCv并改造成.kts版本
android·ide·android studio
yava_free1 小时前
JVM这个工具的使用方法
java·jvm
guokanglun1 小时前
Vue.js动态组件使用
前端·javascript·vue.js
Go4doom1 小时前
vue-cli3+qiankun迁移至rsbuild
前端