浅谈Android之SurfaceFlinger相关介绍(三)

本文解析了 Android Surface 的 Java 层封装原理,包括 SurfaceSession、SurfaceControl 和 Surface 类的创建过程,以及 Canvas 绘制流程。介绍了这些类与 C++ 层的交互方式,并详细分析了图形界面绘制的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3.3 Surface Java层相关封装

主要介绍三个类,对应如下:

Java

C++

SurfaceSession.java

SurfaceComposeClient

对应JNI文件为:

android_view_surfacesession.cpp

SurfaceControl.java

SurfaceControl + Composer

对应JNI文件为:

android_view_surfacecontrol.cpp

Surface.java

Surface

对应JNI文件为:

Android_view_surface.cpp

 

Java类和C++类进行绑定,方法基本都是在Java类放置一个变量,然后通过将JNI调用时创建的C++对象地址保存到Java类对应的变量中,反之如果C++类需要关联Java对象,则在Jni调用时,保存Java类对应的jobject就可以了

 

接下去分析下这三个Java类是如何创建的

 

3.3.1 SurfaceSession的创建

SurfaceSession在Java层的创建很简单,直接newSurfaceSession()就好了,接着看构造函数

  /** Create a new connection with the surface flinger. */

    public SurfaceSession() {

        mNativeClient = nativeCreate();

    }

 

直接调用nativeCreate()创建SurfaceComposeClient并返回其在C++环境的对象地址,如何保存到mNativeClient,接着看JNI实现

// android_view_surfacesession.cpp

static jlong nativeCreate(JNIEnv* env, jclass clazz) {

    SurfaceComposerClient* client = new SurfaceComposerClient();

    client->incStrong((void*)nativeCreate);

    return reinterpret_cast<jlong>(client);

}

 

3.3.2 SurfaceControl的创建

SurfaceControl的创建肯定是要依赖SurfaceSession的,所以构造时必须要传入:

    public SurfaceControl(SurfaceSession session,

            String name, int w, int h, int format, int flags)

                    throws OutOfResourcesException {

        if (session == null) {

            throw new IllegalArgumentException("session must not be null");

        }

        if (name == null) {

            throw new IllegalArgumentException("name must not be null");

        }

 

        mName = name;

        mNativeObject = nativeCreate(session, name, w, h, format, flags);

        if (mNativeObject == 0) {

            throw new OutOfResourcesException(

                    "Couldn't allocate SurfaceControl native object");

        }

 

        mCloseGuard.open("release");

    }

 

接着看其nativeCreate对应Jni的实现

//android_view_surfacecontrol.cpp

static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,

        jstring nameStr, jint w, jint h, jint format, jint flags) {

    ScopedUtfChars name(env, nameStr);

    sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));

    sp<SurfaceControl> surface = client->createSurface(

            String8(name.c_str()), w, h, format, flags);

    if (surface == NULL) {

        jniThrowException(env, OutOfResourcesException, NULL);

        return 0;

    }

    surface->incStrong((void *)nativeCreate);

    return reinterpret_cast<jlong>(surface.get());

}

 

先通过android_view_SurfaceSession_getClient从sessionObj中拿到SurfaceComposerClient对象地址,如何调用client->createSurface创建SurfaceControl并将其地址返回

 

3.3.3 Surface的创建

我们都知道在C++,Surface的创建是通过SurfaceControl.getSurface来得到的,但是在Java层,SurfaceControl好像没有提供这个方法?那Surface如何创建?

 

Java这边的做法是,可以先new Surface,然后调用其copyFrom函数,传入SurfaceControl完成初始化

    public void copyFrom(SurfaceControl other) {

        if (other == null) {

            throw new IllegalArgumentException("other must not be null");

        }

 

        long surfaceControlPtr = other.mNativeObject;

        if (surfaceControlPtr == 0) {

            throw new NullPointerException(

                    "SurfaceControl native object is null. Are you using a released SurfaceControl?");

        }

        long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);

 

        synchronized (mLock) {

            if (mNativeObject != 0) {

                nativeRelease(mNativeObject);

            }

            setNativeObjectLocked(newNativeObject);

        }

    }

 

接着看nativeCreateFromSurfaceControlJNI函数:

static jlong nativeCreateFromSurfaceControl(JNIEnv* env, jclass clazz,

        jlong surfaceControlNativeObj) {

    /*

     * This is used by the WindowManagerService just after constructing

     * a Surface and is necessary for returning the Surface reference to

     * the caller. At this point, we should only have a SurfaceControl.

     */

 

    sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));

    sp<Surface> surface(ctrl->getSurface());

    if (surface != NULL) {

        surface->incStrong(&sRefBaseOwner);

    }

    return reinterpret_cast<jlong>(surface.get());

}

 

Java层都把SurfaceControl的对象地址都传下来了,所以这里就很简单,直接转换后调用getSurface就好了,然后将得到的Surface对象地址返回到java层。

 

 

3.3.4 应用图形界面的绘制(Canvas)

C++ Surface封装类提供了dequeuebuffer用于获取graphicbuffer用于写入应用层图形数据, C++这边可以基于OpenGL ES来输出图形数据,但是OpenGLES对很多应用开发人员来说,门槛还是有的,Android为了简化开发,采用了SKIA 作为默认的图形引擎,并向上提供更加简洁

 

Android应用程序开发对应的绘图表面是Canvas,它就是基于SKIA来实现的,基于Surface获取Canvas很简单:

1)  调用Surface.lockCanvas来获取Canvas

2)  基于得到的Canvas来绘制图形数据

3)  绘制结束后,调用Surface. unlockCanvasAndPost将图形数据甩到SurfaceFlinger

 

下面看代码:

public Canvas lockCanvas(Rect inOutDirty)

            throws Surface.OutOfResourcesException, IllegalArgumentException {

        synchronized (mLock) {

            checkNotReleasedLocked();

            if (mLockedObject != 0) {

                // Ideally, nativeLockCanvas() would throw in this situation and prevent the

                // double-lock, but that won't happen if mNativeObject was updated.  We can't

                // abandon the old mLockedObject because it might still be in use, so instead

                // we just refuse to re-lock the Surface.

                throw new IllegalArgumentException("Surface was already locked");

            }

            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);

            return mCanvas;

        }

}

 

调用nativeLockCanvas跑到JNI对应函数

//android_view_surface.cpp

static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,

        jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {

    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));

 

    if (!isSurfaceValid(surface)) {

        doThrowIAE(env);

        return 0;

    }

 

    Rect dirtyRect;

    Rect* dirtyRectPtr = NULL;

 

    if (dirtyRectObj) {

        dirtyRect.left   = env->GetIntField(dirtyRectObj, gRectClassInfo.left);

        dirtyRect.top    = env->GetIntField(dirtyRectObj, gRectClassInfo.top);

        dirtyRect.right  = env->GetIntField(dirtyRectObj, gRectClassInfo.right);

        dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);

        dirtyRectPtr = &dirtyRect;

    }

 

    ANativeWindow_Buffer outBuffer;

    status_t err = surface->lock(&outBuffer, dirtyRectPtr);

    if (err < 0) {

        const char* const exception = (err == NO_MEMORY) ?

                OutOfResourcesException :

                "java/lang/IllegalArgumentException";

        jniThrowException(env, exception, NULL);

        return 0;

    }

 

    // Associate a SkCanvas object to this surface

    env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format);

 

    SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,

                                         convertPixelFormat(outBuffer.format),

                                         kPremul_SkAlphaType);

    if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {

        info.fAlphaType = kOpaque_SkAlphaType;

    }

 

    SkBitmap bitmap;

    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);

    bitmap.setInfo(info, bpr);

    if (outBuffer.width > 0 && outBuffer.height > 0) {

        bitmap.setPixels(outBuffer.bits);

    } else {

        // be safe with an empty bitmap.

        bitmap.setPixels(NULL);

    }

 

    env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap,

                        reinterpret_cast<jlong>(&bitmap));

 

    if (dirtyRectPtr) {

        SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);

        nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) );

    }

 

    if (dirtyRectObj) {

        env->SetIntField(dirtyRectObj, gRectClassInfo.left,   dirtyRect.left);

        env->SetIntField(dirtyRectObj, gRectClassInfo.top,    dirtyRect.top);

        env->SetIntField(dirtyRectObj, gRectClassInfo.right,  dirtyRect.right);

        env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, dirtyRect.bottom);

    }

 

    // 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();

}

 

这个函数主要做了:

1)      调用surface->lockdequeuebuffer

2)      基于输出图形的大小和格式创建SkImageInfo

3)      创建SKBitmap位图对象,接着调用bitmap.setPixels(outBuffer.bits);将dequeue得到的graphicbuffer作为位图对应的数据buffer

4)      将创建的bitmap作为canvas的关联位图

 

接着看surface->lock的代码:

status_t Surface::lock(

        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)

{

    if (mLockedBuffer != 0) {

        ALOGE("Surface::lock failed, already locked");

        return INVALID_OPERATION;

    }

 

    if (!mConnectedToCpu) {

        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;

    status_t err = dequeueBuffer(&out, &fenceFd);

    ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));

    if (err == NO_ERROR) {

        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

        const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);

        const bool canCopyBack = (frontBuffer != 0 &&

                backBuffer->width  == frontBuffer->width &&

                backBuffer->height == frontBuffer->height &&

                backBuffer->format == frontBuffer->format);

 

        if (canCopyBack) {

            // copy the area that is invalid and not repainted this round

            const Region copyback(mDirtyRegion.subtract(newDirtyRegion));

            if (!copyback.isEmpty())

                copyBlt(backBuffer, frontBuffer, copyback);

        } else {

            // if we can't copy-back anything, modify the user's dirty

            // region to make sure they redraw the whole buffer

            newDirtyRegion.set(bounds);

            mDirtyRegion.clear();

            Mutex::Autolock lock(mMutex);

            for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {

                mSlots[i].dirtyRegion.clear();

            }

        }

 

 

        { // scope for the lock

            Mutex::Autolock lock(mMutex);

            int backBufferSlot(getSlotFromBufferLocked(backBuffer.get()));

            if (backBufferSlot >= 0) {

                Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion);

                mDirtyRegion.subtract(dirtyRegion);

                dirtyRegion = newDirtyRegion;

            }

        }

 

        mDirtyRegion.orSelf(newDirtyRegion);

        if (inOutDirtyBounds) {

            *inOutDirtyBounds = newDirtyRegion.getBounds();

        }

 

        void* vaddr;

        status_t res = backBuffer->lockAsync(

                GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,

                newDirtyRegion.bounds(), &vaddr, fenceFd);

 

        ALOGW_IF(res, "failed locking buffer (handle = %p)",

                backBuffer->handle);

 

        if (res != 0) {

            err = INVALID_OPERATION;

        } else {

            mLockedBuffer = backBuffer;

            outBuffer->width  = backBuffer->width;

            outBuffer->height = backBuffer->height;

            outBuffer->stride = backBuffer->stride;

            outBuffer->format = backBuffer->format;

            outBuffer->bits   = vaddr;

        }

    }

    return err;

}

 

函数首先调用dequeueBuffer获取一个graphic buffer,接着做脏矩形相关判断,最后通过

backBuffer->lockAsync获取graphic buffer对应的本地映射地址,然后保存到outBuffer中

outBuffer->bits   = vaddr;

 

最后看看unlockCanvasAndPost,很简单,最终调用JNI函数nativeUnlockCanvasAndPost:

static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,

        jlong nativeObject, jobject canvasObj) {

    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));

    if (!isSurfaceValid(surface)) {

        return;

    }

 

    // detach the canvas from the surface

    env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap, (jlong)0);

 

    // unlock surface

    status_t err = surface->unlockAndPost();

    if (err < 0) {

        doThrowIAE(env);

    }

}

 

首先将Canvas关联的bitmap置空,接着调用surface->unlockAndPost()

status_t Surface::unlockAndPost()

{

    if (mLockedBuffer == 0) {

        ALOGE("Surface::unlockAndPost failed, no locked buffer");

        return INVALID_OPERATION;

    }

 

    int fd = -1;

    status_t err = mLockedBuffer->unlockAsync(&fd);

    ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);

 

    err = queueBuffer(mLockedBuffer.get(), fd);

    ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",

            mLockedBuffer->handle, strerror(-err));

 

    mPostedBuffer = mLockedBuffer;

    mLockedBuffer = 0;

    return err;

}

 

首先,将当前locked bufferunlock,然后queueBuffer,结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值