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,结束。