Android效能優化篇:從程式碼角度進行優化
通常我們寫程式,都是在專案計劃的壓力下完成的,此時完成的程式碼可以完成具體業務邏輯,但是效能不一定是最優化的。一般來說,優秀的程式設計師在寫完程式碼之後都會不斷的對程式碼進行重構。重構的好處有很多,其中一點,就是對程式碼進行優化,提高軟體的效能。下面我們就從幾個方面來了解Android開發過程中的程式碼優化。
1)靜態變數引起記憶體洩露
在程式碼優化的過程中,我們需要對程式碼中的靜態變數特別留意。靜態變數是類相關的變數,它的生命週期是從這個類被宣告,到這個類徹底被垃圾回收器回收才會被銷燬。所以,一般情況下,靜態變數從所在的類被使用開始就要一直佔用著記憶體空間,直到程式退出。如果不注意,靜態變數引用了佔用大量記憶體的資源,造成垃圾回收器無法對記憶體進行回收,就可能造成記憶體的浪費。
先來看一段程式碼,這段程式碼定義了一個Activity。
private static Resources mResources; @Override protected void onCreate(Bundle state) { super.onCreate(state); if (mResources == null) { mResources = this.getResources(); } }
這段程式碼中有一個靜態的Resources物件。程式碼片段mResources = this.getResources()對Resources物件進行了初始化。這時Resources物件擁有了當前Activity物件的引用,Activity又引用了整個頁面中所有的物件。
如果當前的Activity被重新建立(比如橫豎屏切換,預設情況下整個Activity會被重新建立),由於Resources引用了第一次建立的Activity,就會導致第一次建立的Activity不能被垃圾回收器回收,從而導致第一次建立的Activity中的所有物件都不能被回收。這個時候,一部分記憶體就浪費掉了。
經驗分享:
在實際專案中,我們經常會把一些物件的引用加入到集合中,如果這個集合是靜態的話,就需要特別注意了。當不需要某物件時,務必及時把它的引用從集合中清理掉。或者可以為集合提供一種更新策略,及時更新整個集合,這樣可以保證集合的大小不超過某值,避免記憶體空間的浪費。
2)使用Application的Context
在Android中,Application Context的生命週期和應用的生命週期一樣長,而不是取決於某個Activity的生命週期。如果想保持一個長期生命的物件,並且這個物件需要一個Context,就可以使用Application物件。可以通過呼叫Context.getApplicationContext()方法或者Activity.getApplication()方法來獲得Application物件。
依然拿上面的程式碼作為例子。可以將程式碼修改成下面的樣子。
private static Resources mResources; @Override protected void onCreate(Bundle state) { super.onCreate(state); if (mResources == null) { // mResources = this.getResources(); mResources = this.getApplication().getResources(); } }
在這裡將this.getResources()修改為this.getApplication().getResources()。修改以後,Resources物件擁有的是Application物件的引用。如果Activity被重新建立,第一次建立的Activity就可以被回收了。
3)及時關閉資源
Cursor是Android查詢資料後得到的一個管理資料集合的類。正常情況下,如果我們沒有關閉它,系統會在回收它時進行關閉,但是這樣的效率特別低。如果查詢得到的資料量較小時還好,如果Cursor的資料量非常大,特別是如果裡面有Blob資訊時,就可能出現記憶體問題。所以一定要及時關閉Cursor。
下面給出一個通用的使用Cursor的程式碼片段。
Cursor cursor = null; try{ cursor = mContext.getContentResolver().query(uri,null,null,null,null); if (cursor != null) { cursor.moveToFirst(); // 處理資料 } } catch (Exception e){ e.printStatckTrace(); } finally { if (cursor != null){ cursor.close(); } }
即對異常進行捕獲,並且在finally中將cursor關閉。
同樣的,在使用檔案的時候,也要及時關閉。
4)使用Bitmap及時呼叫recycle()
前面的章節講過,在不使用Bitmap物件時,需要呼叫recycle()釋放記憶體,然後將它設定為null。雖然呼叫recycle()並不能保證立即釋放佔用的記憶體,但是可以加速Bitmap的記憶體的釋放。
在程式碼優化的過程中,如果發現某個Activity用到了Bitmap物件,卻沒有顯式的呼叫recycle()釋放記憶體,則需要分析程式碼邏輯,增加相關程式碼,在不再使用Bitmap以後呼叫recycle()釋放記憶體。
5)對Adapter進行優化
下面以構造ListView的BaseAdapter為例說明如何對Adapter進行優化。
在BaseAdapter類中提供瞭如下方法:
public View getView(int position, View convertView, ViewGroup parent)
當ListView列表裡的每一項顯示時,都會呼叫Adapter的getView方法返回一個View,
來向ListView提供所需要的View物件。
下面是一個完整的getView()方法的程式碼示例。
public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item, null); holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.text.setText("line" + position); return convertView; } private class ViewHolder { TextView text; }
當向上滾動ListView時,getView()方法會被反覆呼叫。getView()的第二個引數convertView是被快取起來的List條目中的View物件。當ListView滑動的時候,getView可能會直接返回舊的convertView。這裡使用了convertView和ViewHolder,可以充分利用快取,避免反覆建立View物件和TextView物件。
如果ListView的條目只有幾個,這種技巧並不能帶來多少效能的提升。但是如果條目有幾百甚至幾千個,使用這種技巧只會建立幾個convertView和ViewHolder(取決於當前介面能夠顯示的條目數),效能的差別就非常非常大了。
6)程式碼“微優化”
當今時代已經進入了“微時代”。這裡的“微優化”指的是程式碼層面的細節優化,即不改動程式碼整體結構,不改變程式原有的邏輯。儘管Android使用的是Dalvik虛擬機器,但是傳統的Java方面的程式碼優化技巧在Android開發中也都是適用的。
下面簡要列舉一部分。因為一般Java開發者都能夠理解,就不再做具體的程式碼說明。
建立新的物件都需要額外的記憶體空間,要儘量減少建立新的物件。
將類、變數、方法等等的可見性修改為最小。
針對字串的拼接,使用StringBuffer替代String。
不要在迴圈當中宣告臨時變數,不要在迴圈中捕獲異常。
如果對於執行緒安全沒有要求,儘量使用執行緒不安全的集合物件。
使用集合物件,如果事先知道其大小,則可以在構造方法中設定初始大小。
檔案讀取操作需要使用快取類,及時關閉檔案。
慎用異常,使用異常會導致效能降低。
如果程式會頻繁建立執行緒,則可以考慮使用執行緒池。
經驗分享:
程式碼的微優化有很多很多東西可以講,小到一個變數的宣告,大到一段演算法。尤其在程式碼Review的過程中,可能會反覆審查程式碼是否可以優化。不過我認為,程式碼的微優化是非常耗費時間的,沒有必要從頭到尾將所有程式碼都優化一遍。開發者應該根據具體的業務邏輯去專門針對某部分程式碼做優化。比如應用中可能有一些方法會被反覆呼叫,那麼這部分程式碼就值得專門做優化。其它的程式碼,需要開發者在寫程式碼過程中去注意。
相關文章
- Android效能優化(1)—webview優化篇Android優化WebView
- Android效能優化篇之服務優化Android優化
- Android 效能優化 ---- 啟動優化Android優化
- Android效能優化----卡頓優化Android優化
- Android效能優化Android優化
- 效能優化篇優化
- js程式碼優化 提高執行效能JS優化
- Android效能優化——圖片優化(二)Android優化
- Android效能優化之佈局優化Android優化
- 使用shouldComponentUpdate進行效能優化優化
- Android Note - 程式碼優化Android優化
- Android效能優——佈局優化Android優化
- Android深度效能優化--記憶體優化(一篇就夠)Android優化記憶體
- Android效能優化——效能優化的難題總結Android優化
- Android效能優化(Memory)Android優化
- Android 效能優化之記憶體優化Android優化記憶體
- IOS效能優化篇iOS優化
- Android效能優化篇之記憶體優化--記憶體洩漏Android優化記憶體
- Android效能優化筆記(一)——啟動優化Android優化筆記
- Android效能優化 筆記Android優化筆記
- Android 效能優化(十二)之我為什麼寫效能優化Android優化
- 前端效能優化JavaScript篇前端優化JavaScript
- Android效能優化(4):UI渲染機制以及優化Android優化UI
- 祖傳程式碼如何優化效能?優化
- 淺談JavaScript程式碼效能優化JavaScript優化
- Python 程式碼的效能優化之道Python優化
- 前端效能優化—js程式碼打包前端優化JS
- 六、Android效能優化之UI卡頓分析之渲染效能優化Android優化UI
- 如何優化程式效能優化
- iOS 效能篇一一UITableView效能優化iOSUIView優化
- 【前端效能優化】vue效能優化前端優化Vue
- Go工程管理 19 | 效能優化:Go 語言如何進行程式碼檢查和優化?Go優化行程
- Linux 效能優化之 CPU 篇 ----- 殭屍程式Linux優化
- C++ 效能優化篇二《影響優化的計算機行為》C++優化計算機
- Android效能優化——列表類控制元件卡頓優化Android優化控制元件
- Android效能優化全方面解析Android優化
- Android App效能優化技能,看這篇就夠了AndroidAPP優化
- iOS效能優化系列篇之“列表流暢度優化”iOS優化
- iOS效能優化系列篇之“優化總體原則”iOS優化