解析Android開發(fā)優(yōu)化之:從代碼角度進(jìn)行優(yōu)化的技巧
通常我們寫程序,都是在項(xiàng)目計(jì)劃的壓力下完成的,此時(shí)完成的代碼可以完成具體業(yè)務(wù)邏輯,但是性能不一定是最優(yōu)化的。一般來(lái)說(shuō),優(yōu)秀的程序員在寫完代碼之后都會(huì)不斷的對(duì)代碼進(jìn)行重構(gòu)。重構(gòu)的好處有很多,其中一點(diǎn),就是對(duì)代碼進(jìn)行優(yōu)化,提高軟件的性能。下面我們就從幾個(gè)方面來(lái)了解Android開發(fā)過程中的代碼優(yōu)化。
1)靜態(tài)變量引起內(nèi)存泄露
在代碼優(yōu)化的過程中,我們需要對(duì)代碼中的靜態(tài)變量特別留意。靜態(tài)變量是類相關(guān)的變量,它的生命周期是從這個(gè)類被聲明,到這個(gè)類徹底被垃圾回收器回收才會(huì)被銷毀。所以,一般情況下,靜態(tài)變量從所在的類被使用開始就要一直占用著內(nèi)存空間,直到程序退出。如果不注意,靜態(tài)變量引用了占用大量?jī)?nèi)存的資源,造成垃圾回收器無(wú)法對(duì)內(nèi)存進(jìn)行回收,就可能造成內(nèi)存的浪費(fèi)。
先來(lái)看一段代碼,這段代碼定義了一個(gè)Activity。
private static Resources mResources;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
if (mResources == null) {
mResources = this.getResources();
}
}
這段代碼中有一個(gè)靜態(tài)的Resources對(duì)象。代碼片段mResources = this.getResources()對(duì)Resources對(duì)象進(jìn)行了初始化。這時(shí)Resources對(duì)象擁有了當(dāng)前Activity對(duì)象的引用,Activity又引用了整個(gè)頁(yè)面中所有的對(duì)象。
如果當(dāng)前的Activity被重新創(chuàng)建(比如橫豎屏切換,默認(rèn)情況下整個(gè)Activity會(huì)被重新創(chuàng)建),由于Resources引用了第一次創(chuàng)建的Activity,就會(huì)導(dǎo)致第一次創(chuàng)建的Activity不能被垃圾回收器回收,從而導(dǎo)致第一次創(chuàng)建的Activity中的所有對(duì)象都不能被回收。這個(gè)時(shí)候,一部分內(nèi)存就浪費(fèi)掉了。
經(jīng)驗(yàn)分享:
在實(shí)際項(xiàng)目中,我們經(jīng)常會(huì)把一些對(duì)象的引用加入到集合中,如果這個(gè)集合是靜態(tài)的話,就需要特別注意了。當(dāng)不需要某對(duì)象時(shí),務(wù)必及時(shí)把它的引用從集合中清理掉。或者可以為集合提供一種更新策略,及時(shí)更新整個(gè)集合,這樣可以保證集合的大小不超過某值,避免內(nèi)存空間的浪費(fèi)。
2)使用Application的Context
在Android中,Application Context的生命周期和應(yīng)用的生命周期一樣長(zhǎng),而不是取決于某個(gè)Activity的生命周期。如果想保持一個(gè)長(zhǎng)期生命的對(duì)象,并且這個(gè)對(duì)象需要一個(gè)Context,就可以使用Application對(duì)象??梢酝ㄟ^調(diào)用Context.getApplicationContext()方法或者Activity.getApplication()方法來(lái)獲得Application對(duì)象。
依然拿上面的代碼作為例子??梢詫⒋a修改成下面的樣子。
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對(duì)象擁有的是Application對(duì)象的引用。如果Activity被重新創(chuàng)建,第一次創(chuàng)建的Activity就可以被回收了。
3)及時(shí)關(guān)閉資源
Cursor是Android查詢數(shù)據(jù)后得到的一個(gè)管理數(shù)據(jù)集合的類。正常情況下,如果我們沒有關(guān)閉它,系統(tǒng)會(huì)在回收它時(shí)進(jìn)行關(guān)閉,但是這樣的效率特別低。如果查詢得到的數(shù)據(jù)量較小時(shí)還好,如果Cursor的數(shù)據(jù)量非常大,特別是如果里面有Blob信息時(shí),就可能出現(xiàn)內(nèi)存問題。所以一定要及時(shí)關(guān)閉Cursor。
下面給出一個(gè)通用的使用Cursor的代碼片段。
Cursor cursor = null;
try{
cursor = mContext.getContentResolver().query(uri,null,null,null,null);
if (cursor != null) {
cursor.moveToFirst();
// 處理數(shù)據(jù)
}
} catch (Exception e){
e.printStatckTrace();
} finally {
if (cursor != null){
cursor.close();
}
}
即對(duì)異常進(jìn)行捕獲,并且在finally中將cursor關(guān)閉。
同樣的,在使用文件的時(shí)候,也要及時(shí)關(guān)閉。
4)使用Bitmap及時(shí)調(diào)用recycle()
前面的章節(jié)講過,在不使用Bitmap對(duì)象時(shí),需要調(diào)用recycle()釋放內(nèi)存,然后將它設(shè)置為null。雖然調(diào)用recycle()并不能保證立即釋放占用的內(nèi)存,但是可以加速Bitmap的內(nèi)存的釋放。
在代碼優(yōu)化的過程中,如果發(fā)現(xiàn)某個(gè)Activity用到了Bitmap對(duì)象,卻沒有顯式的調(diào)用recycle()釋放內(nèi)存,則需要分析代碼邏輯,增加相關(guān)代碼,在不再使用Bitmap以后調(diào)用recycle()釋放內(nèi)存。
5)對(duì)Adapter進(jìn)行優(yōu)化
下面以構(gòu)造ListView的BaseAdapter為例說(shuō)明如何對(duì)Adapter進(jìn)行優(yōu)化。
在BaseAdapter類中提供了如下方法:
public View getView(int position, View convertView, ViewGroup parent)
當(dāng)ListView列表里的每一項(xiàng)顯示時(shí),都會(huì)調(diào)用Adapter的getView方法返回一個(gè)View,
來(lái)向ListView提供所需要的View對(duì)象。
下面是一個(gè)完整的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;
}
當(dāng)向上滾動(dòng)ListView時(shí),getView()方法會(huì)被反復(fù)調(diào)用。getView()的第二個(gè)參數(shù)convertView是被緩存起來(lái)的List條目中的View對(duì)象。當(dāng)ListView滑動(dòng)的時(shí)候,getView可能會(huì)直接返回舊的convertView。這里使用了convertView和ViewHolder,可以充分利用緩存,避免反復(fù)創(chuàng)建View對(duì)象和TextView對(duì)象。
如果ListView的條目只有幾個(gè),這種技巧并不能帶來(lái)多少性能的提升。但是如果條目有幾百甚至幾千個(gè),使用這種技巧只會(huì)創(chuàng)建幾個(gè)convertView和ViewHolder(取決于當(dāng)前界面能夠顯示的條目數(shù)),性能的差別就非常非常大了。
6)代碼“微優(yōu)化”
當(dāng)今時(shí)代已經(jīng)進(jìn)入了“微時(shí)代”。這里的“微優(yōu)化”指的是代碼層面的細(xì)節(jié)優(yōu)化,即不改動(dòng)代碼整體結(jié)構(gòu),不改變程序原有的邏輯。盡管Android使用的是Dalvik虛擬機(jī),但是傳統(tǒng)的Java方面的代碼優(yōu)化技巧在Android開發(fā)中也都是適用的。
下面簡(jiǎn)要列舉一部分。因?yàn)橐话鉐ava開發(fā)者都能夠理解,就不再做具體的代碼說(shuō)明。
創(chuàng)建新的對(duì)象都需要額外的內(nèi)存空間,要盡量減少創(chuàng)建新的對(duì)象。
將類、變量、方法等等的可見性修改為最小。
針對(duì)字符串的拼接,使用StringBuffer替代String。
不要在循環(huán)當(dāng)中聲明臨時(shí)變量,不要在循環(huán)中捕獲異常。
如果對(duì)于線程安全沒有要求,盡量使用線程不安全的集合對(duì)象。
使用集合對(duì)象,如果事先知道其大小,則可以在構(gòu)造方法中設(shè)置初始大小。
文件讀取操作需要使用緩存類,及時(shí)關(guān)閉文件。
慎用異常,使用異常會(huì)導(dǎo)致性能降低。
如果程序會(huì)頻繁創(chuàng)建線程,則可以考慮使用線程池。
經(jīng)驗(yàn)分享:
代碼的微優(yōu)化有很多很多東西可以講,小到一個(gè)變量的聲明,大到一段算法。尤其在代碼Review的過程中,可能會(huì)反復(fù)審查代碼是否可以優(yōu)化。不過我認(rèn)為,代碼的微優(yōu)化是非常耗費(fèi)時(shí)間的,沒有必要從頭到尾將所有代碼都優(yōu)化一遍。開發(fā)者應(yīng)該根據(jù)具體的業(yè)務(wù)邏輯去專門針對(duì)某部分代碼做優(yōu)化。比如應(yīng)用中可能有一些方法會(huì)被反復(fù)調(diào)用,那么這部分代碼就值得專門做優(yōu)化。其它的代碼,需要開發(fā)者在寫代碼過程中去注意。
- Android編程之文件讀寫操作與技巧總結(jié)【經(jīng)典收藏】
- Android編程之OpenGL繪圖技巧總結(jié)
- Android編程常用技巧實(shí)例總結(jié)
- Android編程開發(fā)之性能優(yōu)化技巧總結(jié)
- Android Studio使用小技巧:提取方法代碼片段
- Android Studio使用小技巧:自定義Logcat
- Android開發(fā)技巧之我的菜單我做主(自定義菜單)
- android 為應(yīng)用程序創(chuàng)建桌面快捷方式技巧分享
- Android開發(fā)技巧之像QQ一樣輸入文字和表情圖像
- Android Studio使用小技巧:布局預(yù)覽時(shí)填充數(shù)據(jù)
- Android開發(fā)中常用的一些小技巧
- Android實(shí)用編程技巧代碼總結(jié)
相關(guān)文章
Android開發(fā)實(shí)現(xiàn)帶清空按鈕的EditText示例
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)帶清空按鈕的EditText,結(jié)合具體實(shí)例形式分析了Android實(shí)現(xiàn)EditText清空按鈕功能相關(guān)操作技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-11-11Android模擬器實(shí)現(xiàn)手機(jī)添加文件到sd卡的方法
這篇文章主要介紹了Android模擬器實(shí)現(xiàn)手機(jī)添加文件到sd卡的方法,詳細(xì)分析了Android模擬器添加文件到sd卡的步驟與相關(guān)技巧,需要的朋友可以參考下2016-06-06Android 實(shí)現(xiàn)全屏顯示的幾種方法整理
這篇文章主要介紹了Android 實(shí)現(xiàn)全屏顯示的幾種方法整理的相關(guān)資料,需要的朋友可以參考下2017-03-03強(qiáng)制去除Unity自動(dòng)添加的Android隱私權(quán)限
大家好,本篇文章主要講的是強(qiáng)制去除Unity自動(dòng)添加的Android隱私權(quán)限,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12Android 5秒學(xué)會(huì)使用手勢(shì)解鎖功能
本文講述的是一個(gè)手勢(shì)解鎖的庫(kù),可以定制顯示隱藏宮格點(diǎn)、路徑、并且?guī)в行【艑m格顯示圖,和震動(dòng)!讓你學(xué)會(huì)使用這個(gè)簡(jiǎn)單,高效的庫(kù),好了,具體內(nèi)容詳情大家通過本文學(xué)習(xí)吧2017-12-12Android實(shí)現(xiàn)密碼明密文切換(小眼睛)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)密碼明密文切換,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08Android實(shí)現(xiàn)多次閃退清除數(shù)據(jù)
這篇文章主要介紹了Android實(shí)現(xiàn)多次閃退清除數(shù)據(jù)的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-04-04Android簡(jiǎn)單實(shí)現(xiàn)屏幕下方Tab菜單的方法
這篇文章主要介紹了Android簡(jiǎn)單實(shí)現(xiàn)屏幕下方Tab菜單的方法,簡(jiǎn)單分析了Android實(shí)現(xiàn)tab菜單所涉及的界面布局及功能相關(guān)操作技巧,需要的朋友可以參考下2016-08-08RxJava+Retrofit實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求封裝的方法
Retrofit是當(dāng)前應(yīng)用非常廣泛的網(wǎng)絡(luò)請(qǐng)求框架,通常結(jié)合RxJava來(lái)進(jìn)行網(wǎng)絡(luò)請(qǐng)求,本文將展示一個(gè)采用RxJava+Retrofit的網(wǎng)絡(luò)請(qǐng)求demo,感興趣的可以了解一下2019-04-04Android實(shí)現(xiàn)加載對(duì)話框
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)加載對(duì)話框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-01-01