報告分為兩大部分,第一部分是3.22號在中心五樓會議室討論后的一些需要深入調(diào)研的點,第二部分是android對2D的調(diào)用關(guān)系。
第二部分:
Android中2D圖形引擎流程(以下說明以android2.0及以上版本):
上圖是android的框架圖,在LIBRARIES中有三個模塊是比較重要的。分別是Surface Manager(界面管理),OpenGL|ES,SGL(SGL就是上文提到的skia graphic layer,是android中為應(yīng)用提供的一套2D圖形庫)。
Android的圖形系統(tǒng)采用client和server架構(gòu)。
Client server
因此我們對其的理解可以分成兩個部分,應(yīng)用和界面:
應(yīng)用:2D接口會調(diào)用skia圖形引擎,而skia是軟件實現(xiàn)的。3D接口會調(diào)用OpenGL,OpenGL的庫可以通過軟件實現(xiàn),如果存在硬件庫的話則調(diào)用硬件。軟件庫libagl.so,硬件庫libhgl.so.
此部分的調(diào)研疑問:所有資料幾乎都說skia是軟件實現(xiàn),沒法使用硬件,但是在臺灣的一篇介紹skia的博文中卻提到:skia它可以搭配OpenGL/ES與特定的硬體特征,強(qiáng)化顯示的效果。這部分存在矛盾,需進(jìn)一步驗證(這部分的疑惑已找到,可以參考這篇文章http://blog.csdn.net/yili_xie/archive/2009/11/12/4803565.aspx)。
界面:界面主要是通過sufaceFlinger實現(xiàn)的,在2.0及以上版本,sufaceFlinger直接調(diào)用OpenGL ES API。此時如果存在硬件庫則系統(tǒng)自動調(diào)用硬件庫的api實現(xiàn)硬件加速。
如果系統(tǒng)沒有支持OpenGL ES的硬件,但是能夠支持一些簡單的2D功能。則系統(tǒng)會調(diào)用軟件庫,OPEN GLES軟件庫中有一個copybit的適配文件,此時我們可以在hardware中實現(xiàn)copybit這個文件。系統(tǒng)在自動調(diào)用OpenGL ES軟件庫的時候,,系統(tǒng)通過適配文件找到copybit從而將一些功能通過硬件實現(xiàn)(這些功能主要包括內(nèi)存的拷貝,放大縮小,旋轉(zhuǎn))。
如果系統(tǒng)沒有支持OpenGL ES的硬件,甚至連簡單2D硬件都沒有,sufaceFlinger通過open gl es軟件庫實現(xiàn)。
----------------------------------------------------------------------------------
文章摘自如下:
Android apk 里面的畫圖分為2D和3D兩種:2D是由Skia 來實現(xiàn)的,也就是我們在框架圖上看到的SGL,SGL也會調(diào)用部分opengl 的內(nèi)容來實現(xiàn)簡單的3D效果;3D部分是由OpenGL|ES實現(xiàn)的,OpenGL|ES是Opengl的嵌入式版本,我們先了解一下Android apk的幾種畫圖方式,然后再來來看一看這一整套的圖形體系是怎么建立的。
首先畫圖都是針對提供給應(yīng)用 程序的一塊內(nèi)存填充數(shù)據(jù) , 沒去研究過一個Activity是否就對應(yīng)著底層的一個surface,但是應(yīng)該都是對這塊surface內(nèi)存進(jìn)行操作。因此說穿了就是我們要么調(diào)用2D 的API畫圖,要么調(diào)用3D的API畫圖,然后將畫下來的圖保存在這個內(nèi)存中,最后這個內(nèi)存里面的內(nèi)容會被Opengl渲染以后變?yōu)榭梢栽谄聊簧系南袼匦?息。
一 、Apk應(yīng)用主要關(guān)心的還是這些API的使用,在Android apk里面畫圖有2種方式 [2D]:
1、Simple Graphics in View
就是直接使用Android已經(jīng)實現(xiàn)的一些畫圖操作,比如說images,shapes,colors,pre-defined animation等等,這些簡單的畫圖操作實際上是由skia來提供的2D圖形操作。使用這些預(yù)定義好的操作,我們可以實現(xiàn)諸如貼一張背景圖啊,畫出簡 單地形狀阿,實現(xiàn)一些簡單的動畫之類的操作。這里的簡單可以這么理解,就是我們在這里沒有一筆一畫地構(gòu)造出一個圖形出來,我們只是把我們的Graphic 資源放入View體系中,由系統(tǒng) 來將這些Graphic畫出來。舉個例子:我們現(xiàn)在在Activity里面綁定一個ImageView,我們可以設(shè)置 這 個ImageView的內(nèi)容是我們的picture,然后我們可以讓這個picture整體顏色上來點藍(lán)色調(diào),然后我們還可以為這個ImageView加 入一個預(yù)定義動畫,這樣當(dāng)系統(tǒng)要顯示這個View的時候就會顯示我們的picture,并且會有動畫,并帶有一個藍(lán)色調(diào),我們并沒有自己去定義畫圖操作, 而是將這些內(nèi)容放入View中,由系統(tǒng)來將這些內(nèi)容畫出來。這種方式只能畫靜態(tài)或者極為簡單的2D圖畫,對于實時性很強(qiáng)的動畫,高品質(zhì)的游戲都是沒法實現(xiàn) 的。
2、Canvas
首先我們要明白這個Canvas是一個2D的概念,是在Skia中定義的。也就是說在這個方式下還是說的畫2D圖形。我們可以把這個Canvas理解成系 統(tǒng)提供給我們的一塊內(nèi)存區(qū)域(但實際上它只是一套畫圖的API,真正的內(nèi)存是下面的Bitmap),而且它還提供了一整套對這個內(nèi)存區(qū)域進(jìn)行操作的方法, 所有的這些操作都是畫圖API。也就是說在這種方式下我們已經(jīng)能一筆一劃或者使用Graphic來畫我們所需要的東西了,要畫什么要顯示什么都由我們自己 控制。這種方式根據(jù)環(huán)境還分為兩種:一種就是使用普通View的canvas畫圖,還有一種就是使用專門的SurfaceView的canvas來畫圖。 兩種的主要是區(qū)別就是可以在SurfaceView中定義一個專門的線程來完成畫圖工作,應(yīng)用程序不需要等待View的刷圖,提高性能。前面一種適合處理 量比較小,幀率比較小的動畫,比如說象棋游戲之類的;而后一種主要用在游戲,高品質(zhì)動畫方面的畫圖。下面是這兩種方式的典型sequence :
2.1、View canvas
(1) 定義一個自己的View :class your_view extends View{} ;
(2) 重載View的onDraw方法:protected void onDraw(Canvas canvas){} ;
(3) 在onDraw方法中定義你自己的畫圖操作 ;
ps: 可以定義自己的Btimap:
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
但是必須將這個Bitmap放入View的canvas中,畫的圖才能顯示出來:public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint) ;
invalidate()這個函數(shù)被調(diào)用后,系統(tǒng)會在不久的將來重新調(diào)用onDraw()函數(shù),這個時間很短但并不是可預(yù)知的,我曾經(jīng)在onDraw() 中寫了一個if語句,利用validate讓系統(tǒng)死循環(huán)調(diào)用onDraw刷兩張圖,幾乎上這兩張圖是刷在一起的。
2.2、Surface View Canvas
(1) 定義一個自己的SurfaceView : class your_surfaceview extends SurfaceView implements SurfaceHolder.Callback() {} ;
(2) 實現(xiàn)SurfaceHolder.Callback的3個方法surfaceCreated() surfaceChanged() surfaceDestroyed() ;
(3) 定義自己的專注于畫圖的線程 : class your_thread extends Thread() {} ;
(4) 重載線程的run()函數(shù) [一般在run中定義畫圖操作,在surfaceCreated中啟動這個線程]
(5) 畫圖的過程一般是這樣的:
SurfaceHolder surfaceHolder = getHolder() ; //取得holder,這個holder主要是對surface操作的適配,用戶不具備對surface操作的權(quán)限
surfaceHolder.addCallback(this) ; //注冊實現(xiàn)好的callback
Canvas canvas = surfaceHolder.lockCanvas() ; //取得畫圖的Canvas
/*---------------------------------畫圖
**-------------------------------- 畫圖結(jié)束*/
surfaceHolder.unlockCanvasAndPost() ; //提交并顯示
所有前面3種方式的畫圖的一些例子在SDK上都有,寫得也比較清楚,我這里就不說了,這里寫一下我調(diào)這些代碼過程一些小經(jīng)驗,應(yīng)該主要涉及的是Activity這方面,應(yīng)該以后都用得到:
首先是關(guān)于Eclipse的 :
(1) ctrl + shift + O 可以自動添加需要的依賴包,這功能挺實用的覺得,還有alt + /是語法補(bǔ)全 ;
(2) 代碼中右鍵比較實用的功能有很多,我記得的是F3找類的聲明,F(xiàn)4找類的繼承關(guān)系 ;
(3) 斷點調(diào)試比較方便的,在Eclipse的右上交可以選擇閱讀代碼的方式,還能經(jīng)如debug模式,我現(xiàn)在用到的兩個打log的方式:
Log.e("class", "value : "+ classname) ; //檢測class是否為空指針
Log.e(this.getClass().getName(), "notice message") ;
然后是關(guān)于Activity的 :
(1) 首先盡量把UI的設(shè)計放在XML中實現(xiàn),而不要放在代碼中實現(xiàn),這樣方便設(shè)計,修改和移植 ;
(2) 所有使用到的component都必須在manifest中聲明,不然程序中找不到相應(yīng)的conponet的時候會報錯 ;
(3) 一般每一個Activity都對應(yīng)于一個類,和一個相應(yīng)的布局文件xml ;
(4) 每一個Activity只有使用setContentView()綁定內(nèi)容后才會顯示,而且你才能從這個內(nèi)容(比如xml中)獲取到你需要的元素 ;
(5) res/drawable和res/raw中的元素的區(qū)別是drawable中的元素像素可能會被系統(tǒng)優(yōu)化,而raw中的不會被優(yōu)化 ;
(6) 當(dāng)多個Activity都從res/drawable中獲得同一個元素,如果其中一個修改它的屬性,所有其他的Activity中這個元素的相應(yīng)屬性都會改變 ;
(7) res/anim中保存的是動畫相關(guān)的xml ;
下面我們總結(jié)以下2D畫圖用到的包 :
android.view //畫圖是在View中進(jìn)行的
android.view.animation //定義了一些簡單的動畫效果Tween Animation 和 Frame. Animation
android.graphics //定義了畫圖比較通用的API,比如canvas,paint,bitmap等
android.graphics.drawable //定義了相應(yīng)的Drawable(可畫的東西),比如說BitmapDrawable,PictureDrawable等
android.graphics.drawable.shapes //定義了一些shape
二、了解了2D,我們再來看看3D的畫圖方式。3D畫圖SDK上講得很簡單,只是提了一個通用的方式,就是繼承一個View,然后在這個View里面獲得 Opengl的句柄進(jìn)行畫圖,道理應(yīng)該來說是和2D一樣的,差別就是一個是使用2D的API畫圖,一個是使用3D的。不過因為3D openGl|ES具有一套本身的運行機(jī)制,比如渲染的過程控制等,因此Android為我們提供了一個專門的用在3D畫圖上的 GLSurfaceView。這個類被放在一個單獨的包android.opengl里面,其中實現(xiàn)了其他View所不具備的操作:
(1) 具有OpenGL|ES調(diào)用過程中的錯誤跟蹤,檢查工具,這樣就方便了Opengl編程過程的debug ;
(2) 所有的畫圖是在一個專門的Surface上進(jìn)行,這個Surface可以最后被組合到android的View體系中 ;
(3) 它可以根據(jù)EGL的配置來選擇自己的buffer類型,比如RGB565,depth=16 (這里有點疑問,SurfaceHolder的類型是SURFACE_TYPE_GPU,內(nèi)存就是從EGL分配過來的?)
(4) 所有畫圖的操作都通過render來提供,而且render對Opengl的調(diào)用是在一個單獨的線程中
(5) Opengl的運行周期與Activity的生命周期可以協(xié)調(diào)
下面我們再看看利用GLSurface畫3D圖形的一個典型的Sequence
(1) 選擇你的EGL配置(就是你畫圖需要的buffer類型) [optional] :
setEGLConfigChooser(boolean)
setEGLConfigChooser(EGLConfigChooser)
setEGLConfigChooser(int, int, int, int, int, int)
(2) 選擇是否需要Debug信息 [optional] :
setDebugFlags(int)
setGLWrapper(GLSurfaceView.GLWrapper).
(3) 為GLSurfaceView注冊一個畫圖的renderer : setRenderer(GLSurfaceView.Renderer)
(4) 設(shè)置reander mode,可以為持續(xù)渲染或者根據(jù)命令 來渲染,默認(rèn)是continuous rendering [optional]: setRenderMode(int)
這里有一個要注意的地方就是必須將Opengl的運行和Activity的生命周期綁定在一起,也就是說Activity pause的時候,opengl的渲染也必須pause。另外GLSurfaceView還提供了一個非常實用的線程間交互的函數(shù) queueEvent(Runnable),可以用在主線程和render線程之間的交互,下面就是SDK提供的范例:
class MyGLSurfaceView extends GLSurfaceView {
private MyRenderer mMyRenderer;
public void start() {
mMyRenderer = ...;
setRenderer(mMyRenderer);
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
queueEvent(new Runnable() {
// This method will be called on the rendering
// thread:
public void run() {
mMyRenderer.handleDpadCenter();
}});
return true;
}
return super.onKeyDown(keyCode, event);
}
}
GLSurfaceView是Android提供的一個非常值得學(xué)習(xí) 的類,它實際上是一個如何在View中添加畫圖線程的例子,如何在Java 中使用線程的例子,如何添加事件隊列的例子,一個使用SurfaceView畫圖的經(jīng)典Sequence,一個如何定義Debug信息的例子,覺得把它看懂了可以學(xué)到很多知識 ,具體的源碼在:/framworks/base/opengl/java/android/opengl/GLSurfaceView.java 。
3D的內(nèi)容基本到這里基本講完了,剩下的主要是如何使用Opengl API的問題了,可以看看API demo中簡單的立方體,復(fù)雜的可以看看它那個魔方的實現(xiàn)。下面我們總結(jié)一下3D畫圖需要用到的包:
Android.opengl //主要定義了GLSurfaceView
javax.microedition.khronos.egl //java層的egl接口
javax.microedition.khronos.opengles //opengl API