31. View和SurfaceView
在Android遊戲當中充當主要的除了控制類外就是顯示類,在J2ME中我們用Display和Canvas來實現這些,而Google Android中涉及到顯示的為view類,Android遊戲開發中比較重要和複雜的就是顯示和遊戲邏輯的處理。這裡我們說下android.view.View和android.view.SurfaceView。SurfaceView是從View基類中派生出來的顯示類,直接子類有GLSurfaceView和VideoView,可以看出GL和視頻播放以及Camera網路攝影機一般均使用SurfaceView,到底有哪些優勢呢? SurfaceView可以控製表面的格式,比如大小,顯示在螢幕中的位置,最關鍵是的提供了SurfaceHolder類,使用getHolder方法擷取,相關的有Canvas lockCanvas()
Canvas lockCanvas(Rect dirty) 、void removeCallback(SurfaceHolder.Callback callback)、void unlockCanvasAndPost(Canvas canvas) 控製圖形以及繪製,而在SurfaceHolder.Callback 介面回調中可以通過下面三個抽象類別可以自己定義具體的實現,比如第一個更改格式和顯示畫面。
abstract void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
abstract void surfaceCreated(SurfaceHolder holder)
abstract void surfaceDestroyed(SurfaceHolder holder)
對於Surface相關的,Android底層還提供了GPU加速功能,所以一般即時性很強的應用中主要使用SurfaceView而不是直接從View構建,同時Android123未來後面說到的OpenGL中的GLSurfaceView也是從該類實現。
32. Android程式記憶體管理必讀
很多開發人員都是從J2ME或J2EE上過來的,對於記憶體的使用和理解並不是很到位,Android開發網本次給大家一些架構上的指導,防止出現豆腐渣工程的出現。Android作為以Java語言為主的智能平台對於我們開發一些高效能和品質的軟體來說瞭解Android程式記憶體管理機制是必須的。 Android的Dalvik VM在基礎方面和Sun JVM沒有什麼大的區別僅僅是位元組碼的最佳化,我們要知道什麼時候用gc什麼時候用recycle以及到底用不用finalization,因為Java對記憶體的分配只需要new開發人員不需要顯示的釋放記憶體,但是這樣造成的記憶體泄露問題的幾率反而更高。
1.對於常規開發人員而言需要瞭解 Java的四種引用方式,比如強引用,軟引用,弱引用以及虛引用。一些複雜些的程式在長期運行很可能出現類似OutOfMemoryError的異常。
2.並不要過多的指望gc,不用的對象可以顯示的設定為空白,比如obj=null,這裡Android123提示大家,java的gc使用的是一個有向圖,判斷一個對象是否有效看的是其他的對象能到達這個對象的頂點,有向圖的相對於鏈表、二叉樹來說開銷是可想而知。
3.Android為每個程式分配的對記憶體可以通過Runtime類的totalMemory() freeMemory() 兩個方法擷取VM的一些記憶體資訊,對於系統heap記憶體擷取,可以通過Dalvik.VMRuntime類的getMinimumHeapSize() 方法擷取最小可用堆記憶體,同時顯示釋放軟引用可以調用該類的gcSoftReferences() 方法,擷取更多的運行記憶體。
4.對於多線程的處理,如果並發的線程很多,同時有頻繁的建立和釋放,可以通過concurrent類的線程池解決線程建立的效率瓶頸。
5. 不要在迴圈中建立過多的本地變數。
有關Android和Java的系統效能分析,Android123將在以後的文章中詳細講述如何調試Java分析記憶體泄露以及Android上的gdb調試器分析得出記憶體效能改進。
33. Android中內嵌字型實現個人化
在Android中我們的應用可以靈活的內嵌自己的字型檔,實現各個手機上可以正常的顯示個人化文字,我們都知道TextView的setTypeface方法可以設定目標文字的顯示特性,比如字型、顏色、粗體、斜體等。我們直接找一個TrueTypeFont的字型檔即.ttf,對於Win32系統的使用者可以直接在Windows/fonts檔案夾中能找到很多。比如微軟雅黑就不錯,可是體積太大,由於Android的Assets類有單個檔案1MB體積的限制,我們先找個英文字型做測試。這裡我們將字型檔android123.ttf放到工程的assets檔案夾的fonts目錄中。
Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/android123.ttf");
TextView tv = (TextView)findViewById(R.id.text);
tv.setTypeface(tf); //設定TextView的風格
tv.setText("CWJ Test");
tv.setTextSize(12);
tv.setTextColor(Color.RED);
34. 擷取和設定ListView的選擇項
擷取當前選中項 int curPos = listView.getFirstVisiblePosition(); 當然是用getItemAtPosition(int nPos)方法也可以 ,設定當前選擇位置 listView.setSelectedPosition(lastPos); 對於基於AbsListView為基類的ListView等控制項均可以使用這種方法。
35. android.text.format檔案大小和日期解析類
很多網友可能直接將自己的J2ME項目生硬的移植到Android平台,其實Google為我們提供好了檔案大小和時間日期解析類,它位於android.text.format這個包中,它提供了強大的標準化解析方法:
1. IP位址解析類 在android.text.format.Formatter中提供了String formatIpAddress(int addr) 這個方法可以輕鬆方便的將socket中的int型轉成類似127.0.0.1的IP格式,需要注意的是Linux平台的位元組順序,即小位元組序、低位元組序little-endian。
2. 檔案大小解析類 細心的網友可能還看到了android.text.format.Formatter中的formatFileSize方法,該方法String formatFileSize (Context context, long number) ,第二個參數是long型,一般為File對象的最後修改時間或建立時間的方法,最終返回類似 12KB、5Bytes的值,20MB的字串。
3. 日期時間解析類 ,該類位於android.text.format.DateFormat這個package中,該類提供了Java中的三種時間對象,Android123提示大家下面三種方法為靜態可以直接調用,如下:
final static CharSequence format(CharSequence inFormat, Date inDate) //傳入Date對象
Given a format string and a Date object, returns a CharSequence containing the requested date.
final static CharSequence format(CharSequence inFormat, Calendar inDate) //Calendar對象
Given a format string and a Calendar object, returns a CharSequence containing the requested date.
final static CharSequence format(CharSequence inFormat, long inTimeInMillis) //long對象
Given a format string and a time in milliseconds since Jan 1, 1970 GMT, returns a CharSequence containing the requested date.
我們可能看到了第一個參數均為inFormat這是一個CharSequence介面的String類型,它提供了靈活的時間格式解析字串描述,Android開發網提示大家注意大小寫要區分,如
April 6, 1970 at 3:23am 例子,那麼inFormat參數的寫法和最終執行的結果如下對照,下面就以Android123的CWJ生日為例子如下
"MM/dd/yy h:mmaa" -> "11/03/87 11:23am"
"MMM dd, yyyy h:mmaa" -> "Nov 3, 1987 11:23am"
"MMMM dd, yyyy h:mmaa" -> "November 3, 1987 11:23am"
"E, MMMM dd, yyyy h:mmaa" -> "Tues, November 3, 1987 11:23am"
"EEEE, MMMM dd, yyyy h:mmaa" -> "Tuesday, Nov 3, 1987 11:23am"
對於判斷一個時間是否為24小時制式可以通過android.text.format.DateFormat類的static boolean is24HourFormat(Context context)方法來判斷。
36. Android代碼效能最佳化技巧
目前來說Android 2.2的JIT效能有了本質的提高,不過對於老版本的程式提高Java執行效率還有很多語言特點來說,今天Android123提到的不是文法糖,而是基礎的問題,對於Java 1.5之後將會有明顯的改進。下面的例子來自SDK:
static class Foo {
int mSplat;
}
Foo[] mArray = ...
上面的靜態類Foo的執行效果和效能,我們分三個方法zero、one和two來做對比。
public void zero() { //大多數人可能簡單直接這樣寫
int sum = 0;
for (int i = 0; i < mArray.length; ++i) {
sum += mArray[i].mSplat;
}
}
public void one() { //通過本機物件改進效能
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
}
public void two() { //推薦的方法,通過Java 1.5的新文法特性可以大幅改進效能
int sum = 0;
for (Foo a : mArray) {
sum += a.mSplat;
}
}
zero() is slowest, because the JIT can't yet optimize away the cost of getting the array length once for every iteration through the loop.
one() is faster. It pulls everything out into local variables, avoiding the lookups. Only the array length offers a performance benefit.
two() is fastest for devices without a JIT, and indistinguishable from one() for devices with a JIT. It uses the enhanced for loop syntax introduced in version 1.5 of the Java programming language.
37. Android開發注意點 Part One
Android已經的很多細節問題我們通過平台開發總結不斷完善這個列表,如果你有相關的內容可以聯絡 android123@163.com .
一、AssetManager - 已知單個檔案處理不能大於1MB,所以如果資源很大,建議使用Zip格式壓縮存放。
二、ScrollView中嵌入ListView - 這個作法可能會出現你的ListView僅僅顯示1行半。
三、Android內建的Zip處理類對檔案名稱編碼無法識別,也沒有提供顯示的設定方法,在zlib中寫死了。
四、使用一些資來源物件記住關閉,比如對於檔案流對象最後
FileOutputStream os = xxx;
try {
//dosomething
} finally {
os.close(); //顯示的使用finally關閉檔案對象。
}
對於Cursor而言,在移動位置時首先判斷Cursor是否為空白,最終使用完仍然需要 close方法,如果重用,可以使用deactivate方法釋放當前資源,通過requery方法重新查詢。
五、SDK中標記為 deprecated 字樣的,常規情況下是有更好的方法可以替代,短期內可以放心使用。這些方法一般高版本的SDK都可以向上相容,目前尚未發現Android放棄某些API的支援。
六、Notification的Intent無法傳遞到目標的Activity,Service和Broardcast沒有測試過,中途需要通過PendingIntent,可能這裡出現了問題。
38. Android上HTTP協議通訊狀態擷取
通常情況下輕量級的Http傳輸Android平台可以直接使用Sun Java的HttpURLConnection類方法處理,比如果自己定義一次請求header可以通過setRequestProperty設定,而我們需要擷取的Http Web Server狀態可以通過HttpURLConnection.getResponseCode() 的方法擷取。
當然Http協議傳回值常見的有 200 為成功,400為請求錯誤,404為未找到,500為伺服器內部錯誤,403無權查看,302為重新導向等等。
對於Android平台提供更完善的Apache類有HttpClient 、HttpPost、HttpResponse、HttpGet和HttpEntity,其中對於資料前序header構造通過HttpEntity,而返回狀態值可以通過HttpResponse擷取。
有關Android用戶端和Server通訊類相關的開發我們將會在以後文章中做大量執行個體介紹。
39. Android布局Java代碼構造法
一般情況下對於Android程式布局我們往往使用XML檔案來編寫,這樣可以提高開發效率,但是考慮到代碼的安全性以及執行效率,可以通過Java代碼執行建立,雖然Android編譯過的xml是二進位的,但是載入xml解析器的效率對於資源佔用還是比較大的,一般一個簡單的TextView,比如
<TextView
android:id="@+id/textControl "
android:layout_width="100px"
android:layout_height="wrap_content" />
可以等價於下面的Java代碼:
LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(100, LayoutParams.WRAP_CONTENT); //寬度為100px,高為自適應最小的高度
// setOrientation(VERTICAL); 設定布局為垂直
TextView textControl = new TextView(this);//如果從一個XXXLayout.,比如LinearLayout為View的基類時這裡this應該換成為建立改類的Context
textControl.setText("Android開發網歡迎您");
addView( textControl, textParams );
當然Java處理效率比XML快得多,但是對於一個複雜介面的編寫,可能需要一些套嵌考慮,如果你思維靈活的話,使用Java代碼來布局你的Android應用程式是一個更好的方法。
40. 測試Android軟體效能主要方法
對於Android平台上軟體的效能測試可以通過以下幾種方法來分析效率瓶頸,目前Google在Android軟體開發過程中已經引入了多種測試載入器包,比如Unit測試工程,調試類,還有模擬器的Dev Tools都可以直接反應執行效能。
1. 在模擬器上的Dev Tools可以啟用螢幕顯示當前的FPS,CPU使用率,可以協助我們測試一些3D圖形介面的效能。
2. 一般涉及到網路應用的程式,在效率上和網速有很多關係,這裡需要多次的調試才能實際瞭解。
3. 對於邏輯演算法的效率執行,我們使用Android上最普遍的,計算執行時間來查看:
long start = System.currentTimeMillis();
//android開發網提示這裡做實際的處理do something
long duration = System.currentTimeMillis() - start;
最終duration儲存著實際處理該方法需要的毫秒數。這裡類似Win32上的GetTickCount,在Win 32和Symbian上都提供了高精度的效能計數器和低階計時器,這裡在Dalvik VM上的Java層這種方法對於一般的應用足以。
4. GC效率跟蹤,如果你執行的應用比較簡單,可以在DDMS中查看下Logcat的VM釋放記憶體情況,大概類比下那些地方可以快取資料或改進演算法的。
5. 線程的使用和同步,Android平台上給我們提供了豐富的多任務同步方法,但在深層上並沒有過多的比如自旋鎖等進階應用程式,不過對於Service和appWidget而言,他們實際的產品中都應該以多線程的方式處理,以釋放CPU時間,對於線程和堆記憶體的查看這些都可以在DDMS中看到。
更多的調試和效能測試方法Android123將在以後的內容中出現。