標籤:
效能最佳化之Java(Android)代碼最佳化
本文為Android效能最佳化的第三篇——Java(Android)代碼最佳化。主要介紹Java代碼中效能最佳化方式及網路最佳化,包括緩衝、非同步、延遲、資料存放區、演算法、JNI、邏輯等最佳化方式。(時間倉促,後面還會繼續完善^_*)
目前效能最佳化專題已完成以下部分:
效能最佳化總綱——效能問題及效能調優方式
效能最佳化第四篇——移動網路最佳化
效能最佳化第三篇——Java(Android)代碼最佳化
效能最佳化第二篇——布局最佳化
效能最佳化第一篇——資料庫效能最佳化
效能最佳化執行個體
1、降低執行時間
這部分包括:緩衝、資料存放區最佳化、演算法最佳化、JNI、邏輯最佳化、需求最佳化幾種最佳化方式。
(1). 緩衝
緩衝主要包括對象緩衝、IO緩衝、網路緩衝、DB緩衝,對象緩衝能減少記憶體的分配,IO緩衝減少磁碟的讀寫次數,網路緩衝減少網路傳輸,DB緩衝較少Database的訪問次數。
在記憶體、檔案、資料庫、網路的讀寫速度中,記憶體都是最優的,且速度數量級差別,所以盡量將需要頻繁訪問或訪問一次消耗較大的資料存放區在緩衝中。
Android中常使用緩衝:
a. 線程池
b. Android圖片緩衝,Android圖片Sdcard緩衝,資料預取緩衝
c. 訊息緩衝
通過handler.obtainMessage複用之前的message,如下:
| 1 |
handler.sendMessage(handler.obtainMessage(0, object)); |
d. ListView緩衝
e. 網路緩衝
資料庫緩衝http response,根據http頭資訊中的Cache-Control域確定緩衝到期時間。
f. 檔案IO緩衝
使用具有緩衝策略的輸入資料流,BufferedInputStream替代InputStream,BufferedReader替代Reader,BufferedReader替代BufferedInputStream.對檔案、網路IO皆適用。
g. layout緩衝
h. 其他需要頻繁訪問或訪問一次消耗較大的資料緩衝
(2). 資料存放區最佳化
包括資料類型、資料結構的選擇。
a. 資料類型選擇
字串拼接用StringBuilder代替String,在非並發情況下用StringBuilder代替StringBuffer。如果你對字串的長度有大致瞭解,如100字元左右,可以直接new StringBuilder(128)指定初始大小,減少空間不夠時的再次分配。
64位類型如long double的處理比32位如int慢
使用SoftReference、WeakReference相對正常的強應用來說更有利於系統記憶體回收
final類型儲存在常量區中讀取效率更高
LocalBroadcastManager代替普通BroadcastReceiver,效率和安全性都更高
b. 資料結構選擇
常見的資料結構選擇如:
ArrayList和LinkedList的選擇,ArrayList根據index取值更快,LinkedList更占記憶體、隨機插入刪除更快速、擴容效率更高。一般推薦ArrayList。
ArrayList、HashMap、LinkedHashMap、HashSet的選擇,hash系列資料結構查詢速度更優,ArrayList儲存有序元素,HashMap為索引值對資料結構,LinkedHashMap可以記住加入次序的hashMap,HashSet不允許重複元素。
HashMap、WeakHashMap選擇,WeakHashMap中元素可在適當時候被系統記憶體回收行程自動回收,所以適合在記憶體緊張型中使用。
Collections.synchronizedMap和ConcurrentHashMap的選擇,ConcurrentHashMap為細分鎖,鎖粒度更小,並發效能更優。Collections.synchronizedMap為對象鎖,自己添加函數進行鎖控制更方便。
Android也提供了一些效能更優的資料類型,如SparseArray、SparseBooleanArray、SparseIntArray、Pair。
Sparse系列的資料結構是為key為int情況的特殊處理,採用二分尋找及簡單的數組儲存,加上不需要泛型轉換的開銷,相對Map來說效能更優。不過我不太明白為啥預設的容量大小是10,是做過資料統計嗎,還是說現在的記憶體最佳化不需要考慮這些東西,寫16會死嗎,還是建議大家根據自己可能的容量設定初始值。
(3). 演算法最佳化
這個主題比較大,需要具體問題具體分析,盡量不用O(n*n)時間複雜度以上的演算法,必要時候可用空間換時間。
查詢考慮hash和二分,盡量不用遞迴。可以從結構之法 演算法之道或微軟、Google等面試題學習。
(4). JNI
Android應用程式大都通過Java開發,需要Dalvik的JIT編譯器將Java位元組碼轉換成本地代碼運行,而本地代碼可以直接由裝置管理員直接執行,節省了中間步驟,所以執行速度更快。不過需要注意從Java空間切換到本地空間需要開銷,同時JIT編譯器也能產生最佳化的本地代碼,所以糟糕的本地代碼不一定效能更優。
這個最佳化點會在後面單獨用一片部落格介紹。
(5). 邏輯最佳化
這個不同於演算法,主要是理清程式邏輯,減少不必要的操作。
(6). 需求最佳化
這個就不說了,對於sb的需求可能帶來的效能問題,只能說做為一個合格的程式員不能只是執行者,要學會說NO。不過不能拿這種介面敷衍產品經理哦。
2、非同步,利用多線程提高TPS
充分利用多核Cpu優勢,利用線程解決密集型計算、IO、網路等操作。
關於多線程可參考:Java線程池
在Android應用程式中由於系統ANR的限制,將可能造成主線程逾時操作放入另外的背景工作執行緒中。在背景工作執行緒中可以通過handler和主線程互動。
3、提前或延遲操作,錯開時間段提高TPS
(1) 延遲操作
不在Activity、Service、BroadcastReceiver的生命週期等對回應時間敏感函數中執行耗時操作,可適當delay。
Java中延遲操作可使用ScheduledExecutorService,不推薦使用Timer.schedule;
Android中除了支援ScheduledExecutorService之外,還有一些delay操作,如
handler.postDelayed,handler.postAtTime,handler.sendMessageDelayed,View.postDelayed,AlarmManager定時等。
(2) 提前操作
對於第一次調用較耗時操作,可統一放到初始化中,將耗時提前。如得到壁紙wallpaperManager.getDrawable();
4、網路最佳化
更多見 效能最佳化第四篇——移動網路最佳化
以下是網路最佳化中一些用戶端和伺服器端需要盡量遵守的準則:
a. 圖片必須緩衝,最好根據機型做圖片做圖片適配
b. 所有http請求必須添加httptimeout
c. 開啟gzip壓縮
d. api介面資料以json格式返回,而不是xml或html
e. 根據http頭資訊中的Cache-Control及expires域確定是否緩衝請求結果。
f. 確定網路請求的connection是否keep-alive
g. 減少網路請求次數,伺服器端適當做請求合并。
h. 減少重新導向次數
i. api介面伺服器端回應時間不超過100ms
google正在做將移動端網頁速度降至1秒的項目,關注中https://developers.google.com/speed/docs/insights/mobile
效能最佳化之Java(Android)代碼最佳化