淺談Android之SurfaceFlinger相關介紹(三)
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,結束。
相關推薦
淺談Android之SurfaceFlinger相關介紹(三)
3.3 Surface Java層相關封裝 主要介紹三個類,對應如下: Java C++ SurfaceSession.java SurfaceComposeClient 對應JNI檔案為: android_view_surfacesession.cpp
android之螢幕適配(三)實踐dimens.xml尺寸適配不同的平板
android3.2以後,為了提供更精準的對佈局檔案的控制,可以通過為資原始檔(res目錄下檔案)增加字尾來指定該資料夾裡的xml佈局檔案或color.xml,string.xml是為哪種大小的螢幕使用。 第一種字尾:sw<N>dp,如layout-sw600
XCode主介面之導航區域介紹(三)
(1)測試導航 測試導航的用途:這個導航面板統一顯示程式碼白盒測試結果,測試用例執行情況,以及快捷執行測試用例。在這個區域,當滑鼠指標移動到測試用例最右邊時就會出現一個執行按鈕,單擊這個按鈕測試就會自動開始執行。如果測試成功就會在相應的用例旁邊出現一個綠色的勾,
淺談Android之Activity 視窗顯示流程介紹(二)
7.3 Activity Décorview佈局(layout) Measure確定Décor View以及child views的大小,layout則是確定child view在其parent view中的顯示區域,只有layout結束,view的left,right,t
淺談Android之Activity 視窗顯示流程介紹(一)
7 Activity 視窗顯示流程介紹 Activity 視窗顯示,其實就是Décor View繪製並顯示的過程,但是在繪製之前,Décor View需要先做下面兩件事情: 1) 確定Décor View的大小 2) 對Décor View進行佈局操作,也就是確定Déc
淺談Android之Activity Decor View建立流程介紹
6 Activity DecorView建立流程介紹 上頭已經完整的介紹了Activity的啟動流程,Activity是如何繫結Window,Window的décor view是如何通過ViewRootImpl與WMS建立關聯的,也就是說,整個框架已經有了,唯一缺的就是Ac
淺談Android之Activity觸控事件傳輸機制介紹
8 Activity觸控事件傳輸機制介紹 當我們觸控式螢幕幕的時候,程式會收到對應的觸控事件,這個事件是在app端去讀取的嗎?肯定不是,如果app能讀取,那會亂套的,所以app不會有這個許可權,系統按鍵的讀取以及分發都是通過WindowManagerService來完成
ASP.NET之旅--淺談Asp.net的執行機制(二)
上節中我們從Http請求在Asp.net中的執行過程進行了分析,但是對於真正核心的東西我們並沒有說明,那接下來我們將問題上拋,從底層類和物件的建立層面上來看Asp.net的執行機制。 三、Asp.net底層執行機制 1、理解HTTP.SYS
淺談Android之App視窗檢視管理
5 App視窗檢視管理 WindowManagerGlobal負責管理App所有要新增到WMS的視窗,介面即為上頭的addView 首先,對於App本地視窗來說,其最核心的資料無非就兩個,一個是Window Parameters,另一個就是視窗的DécorView,一個負
Android之---RecycleView簡單介紹(各種用法的簡介)
RecycleView簡單介紹(各種用法的簡介) 概述 RecyclerView出現已經有一段時間了,相信大家肯定不陌生了,大家可以通過匯入support-v7對其進行使用。 據官方的介紹,該控制元件用於在有限的視窗中展示大量資料集,其實這樣功能的控
01-Linux之Nginx 相關介紹(Nginx是什麽?能幹嘛?)--轉載
透明 必須 負載 上網 f5負載均衡 線程 平臺 map .cn Linux之Nginx 相關介紹 原文地址 Nginx的產生 沒有聽過Nginx?那麽一定聽過它的"同行"Apache吧!Nginx同Apache一樣都是一種WEB服務器。基於REST
Android Studio之Activity切換動畫(三)
文章 oid out size ref intel tar studio anim 1、上一篇文章“Android Studio之多個Activity的滑動切換(二)”中實現了多個activity之間的滑動切換,可是新切換出的activity大多是從右側進入 2、我們能
【SSH 基礎】淺談Hibernate關系映射(3)
區別 ack 增加 ans 存儲結構 mil pro 映射 方向 繼上篇博客 一對多關聯映射(單向) 上面我們介紹了多對一,我們反過來看一對多不就是多對一嗎?那還用再進行不同的映射嗎?有什麽區別嗎?一對多和多對一映射原理是一致的,存儲是同樣的。也就是生成的數據庫
android application類簡單介紹(一)
cati theme text color raw sdn water bsp public 每次應用程序執行時。應用程序的application類保持實例化的狀態。通過擴展applicaiton類,能夠完畢下面3項工作: 1.對android執行時廣播的應用程序
Java淺談數組之內存分析(二)
說明 變量賦值 com logs .info clas code new blog 引用類型的數組的初始化 1數組元素是引用時的內存分析 package com.java.array; class Person{ public int age;//年齡
Java淺談數組之內存分析(一)
靜態 引用變量 ati static ges 組元 strong 淺談 nbsp 數組的內存 1.內存中的數組 數組是一種引用內存,數組引用變量只是一個引用,數組元素和數組變量在內存裏是分開存放的。 實際的數組對象被存放在堆內存(heap)中,數組的引用變量被存儲在棧內存中
再和“面向對象”談戀愛 - 對象相關概念(二)
是個 DG 證件 就是 原型對象 了無 結果 弟弟 IV 上一篇文章把對象的概念講解了一下,這篇文章要重點解釋最讓大家犯迷糊的一些概念,包括 構造函數 實例 繼承 構造函數的屬性與方法(私有屬性與方法) 實例的屬性與方法(共享屬性與方法) prototype(原型) _
淺談數據庫集群(一)
以及 you sso 導致 pac sdn 大型 img watermark 現在,隨著上網人數的激增,一些大型的網站開始使用數據庫集群來提高數據庫的可靠性和數據庫的性能。那麽在介紹數據庫集群之前首先需要弄清楚幾個問題。 1.為什麽要用數據庫集群 (1)
淺談算法,一些感悟(1)
一個 最重要的 情況 更多 項目 通過 時間 清晰 困難 最近看到好幾個同學在學算法,看了一些書,另外跟一個算法較好的同學討論了一下,若有所悟,作此文,以求各位大神指教; 現在看到好多同學學算法,可是,事實上看起來,真正明白理解了算法是一種什麽東西的極少,很多都是為了參加
淺談C語言中的布林(bool)型別
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!