1、使用優(yōu)化過(guò)的數(shù)據(jù)容器。
在Android framework下,建議使用優(yōu)化過(guò)的數(shù)據(jù)容器比如:SparseArray,SparseBooleanArray,LongSparseArray。通用的HashMap實(shí)現(xiàn)的內(nèi)存使用率非常的低,因?yàn)樗枰獮槊恳粋€(gè)mApping創(chuàng)建一個(gè)分離的entry object。另外,SparseArray類(lèi)避免了系統(tǒng)對(duì)有些key的自動(dòng)裝箱,因而帶來(lái)了更高的效率。
2、注意內(nèi)存的開(kāi)銷(xiāo)。

注意你使用的語(yǔ)言和第三方庫(kù)的成本和開(kāi)銷(xiāo),要自始至終的將這些因素考慮在你的程序設(shè)計(jì)中。通常,有些事情表面上看著沒(méi)什么問(wèn)題但實(shí)際上的開(kāi)銷(xiāo)讓人驚嘆。比如: ·枚舉相對(duì)于靜態(tài)常量來(lái)說(shuō),需要兩倍甚至更多的內(nèi)存。你應(yīng)該完全避免在Android中使用枚舉。 ·每一個(gè)在JAVA中的類(lèi)(包括匿名內(nèi)部類(lèi))使用大約500 bytes的代碼量。 ·每一個(gè)類(lèi)的實(shí)例擁有12-16 bytes的RAM消耗。 ·放置一個(gè)單獨(dú)的實(shí)體到HashMap中,一個(gè)額外加的實(shí)體對(duì)象分配需要花費(fèi)32 bytes。
3、關(guān)于代碼的抽象
抽象是一個(gè)好的編程的基礎(chǔ),因?yàn)槌橄罂梢蕴岣叽a的靈活性和可維護(hù)性。然而抽象也帶來(lái)了一定的花銷(xiāo),一般情況下,他們有更多的代碼需要執(zhí)行,需要更多的時(shí)間和更多RAM來(lái)將這些代碼映射到內(nèi)存中。因此,如果你的抽象不能帶來(lái)巨大的好處,你就應(yīng)該割掉你的贅肉。
4、避免依賴(lài)注入框架
雖然注入框架給我們帶來(lái)了很多方便,但是在使用這些框架的時(shí)候,框架需要花費(fèi)很多時(shí)間來(lái)掃描我們自己寫(xiě)的代碼,甚至?xí)⒛男┠愀静恍枰臇|西也加載到內(nèi)存中。
5、小心的使用擴(kuò)展庫(kù)
很多擴(kuò)展庫(kù)的代碼不是針對(duì)手機(jī)環(huán)境開(kāi)發(fā)的,可能在用到移動(dòng)客戶(hù)端的時(shí)候會(huì)導(dǎo)致很低的效率。因此在使用之前,需要評(píng)估一下其占用內(nèi)存的大小。
即使庫(kù)針對(duì)手機(jī)開(kāi)發(fā),也會(huì)有潛在的危險(xiǎn),因?yàn)槊恳粋€(gè)Library做的事情不盡相同。比如,一個(gè)Library使用nano protobufs而另一個(gè)使用micro protobufs。現(xiàn)在,在你的app中就有兩個(gè)protobuf。類(lèi)似情況經(jīng)常發(fā)生。
6、使用混淆器移除不必要的代碼
ProGuard工具通過(guò)移除無(wú)用代碼,使用語(yǔ)意模糊來(lái)保留類(lèi),字段和方法來(lái)壓縮,優(yōu)化和混淆代碼。可以使你的代碼更加完整,更少的RAM 映射頁(yè)。
7、使用多個(gè)進(jìn)程(注意是process 不是 thread ok?)
如果這適合你的app,可能幫助你管理你的app的內(nèi)存就是將你的app多個(gè)部分分配到多個(gè)進(jìn)程中。該技術(shù)必須小心使用并且大多數(shù)應(yīng)用不應(yīng)該運(yùn)行在多個(gè)進(jìn)程下。這個(gè)技術(shù)的主要應(yīng)用是后臺(tái)工作跟天臺(tái)工作一樣重要的情況下。典型應(yīng)用就是:當(dāng)音樂(lè)播放器從服務(wù)器下載并長(zhǎng)時(shí)間播放音樂(lè),一般將其分為兩個(gè)進(jìn)程:一個(gè)是UI,另一個(gè)位置后臺(tái)服務(wù)的運(yùn)行。
like this:
<service android:name=".PlaybackService"
android:process=":background" />
process后面需要記住要有個(gè)":",這表示該進(jìn)程屬于你的app。一般情況下,一塊基本的空進(jìn)程需要的內(nèi)存大小在1.4m左右。
adb shell dumpsys meminfo com.example.android.apis:empty
8、基本性能優(yōu)化方法的基本原則:
1)不要做你不必要的工作;
2)不要申請(qǐng)不必要的內(nèi)存;
例如,你明明知道一個(gè)方法返回一個(gè)String之后,你需要對(duì)這個(gè)String重新進(jìn)行修改,那么就不要返回一個(gè)String,返回一個(gè)StringBuffer會(huì)是你更好的選擇。
再比如,使用int比使用Integer占用更少的空間。這個(gè)大家肯定都是曉得的。
數(shù)組比一個(gè)Map擁有更好的性能。
如果你的方法不需要訪問(wèn)類(lèi)字段,那么讓你的方法是static的吧,這將會(huì)帶來(lái)15%-20%速度的提升。
對(duì)于常量,請(qǐng)盡量使用static and final定義。如果使用final定義常量之后,會(huì)減少編譯器在類(lèi)生成時(shí)初始化<clinit>方法調(diào)用時(shí)對(duì)常量的存儲(chǔ),對(duì)于int型常量,將會(huì)直接使用其數(shù)值來(lái)進(jìn)行替換,而對(duì)于String對(duì)象將會(huì)使用相對(duì)廉價(jià)的“string constant”指令來(lái)替換字段查找表。雖然這個(gè)方法并不完全對(duì)所有類(lèi)型都有效,但是,將常量聲明為static final絕對(duì)是一個(gè)好的做法。
避免Getters/Setters。雖然在一般的面向?qū)ο蟮脑O(shè)計(jì)模式中使用Getter和Setter是稀松平常的事情,但是在Android中使用getters/Setters是一個(gè)非常糟糕的主意,方法的調(diào)用相對(duì)于直接查找字段來(lái)說(shuō)十分的昂貴。在沒(méi)有JIT的情況下,直接對(duì)字段進(jìn)行訪問(wèn)要比通過(guò)Getter訪問(wèn)快了近3倍。在有JIT的情況下,前者比后者快近7倍。
使用最新的循環(huán)方式。比如增強(qiáng)for。
避免使用浮點(diǎn)類(lèi)型。在某些可以的情況下,將浮點(diǎn)替換成整型數(shù)據(jù),然后進(jìn)行計(jì)算會(huì)得到更精確的結(jié)果和更快的速度。
小心使用Native Methods。這里需要糾正的是,Native 方法并不一定能提高你應(yīng)用的速度,有些甚至?xí)虾笸龋驗(yàn)椋紫葋?lái)說(shuō)就需要一部分開(kāi)銷(xiāo)在Java-native transition上,而且JIT并不能對(duì)其進(jìn)行優(yōu)化。另外你需要為每個(gè)你想要在其上運(yùn)行的系統(tǒng)結(jié)構(gòu)上進(jìn)行編譯;即便是同一個(gè)處理器上,你也可能需要多個(gè)版本,比如為G1上的ARM處理器編譯的就不能很好的在Nexus One的ARM上運(yùn)行。Native代碼最主要的用途是,你已經(jīng)有了很多native 代碼,并且你迫切希望接入Android中。而不是使用Native Method來(lái)提高你應(yīng)用中某部分代碼的運(yùn)行速度。
對(duì)于效率的提高除了使用遵守上面兩條外基本準(zhǔn)則外,選擇合適的算法和數(shù)據(jù)結(jié)構(gòu)也是非常關(guān)鍵的。
9、關(guān)于UI上的一些問(wèn)題
Hierarchy Viewer 通過(guò)他,可以看到你自己的Layout文件存在的問(wèn)題。你可以看到你的Layout每一部分計(jì)算,布局,渲染所需要的時(shí)間。盡量的使你的Layout扁平話,深度最好保持 在三層之內(nèi) 。 RelativeLayout 是解決使用LinearLayout堆疊多層問(wèn)題的利劍。那些為了方便 使 用LinearLayout的layout_weight屬性 的哥們,需要重點(diǎn)注意,這個(gè)屬性真的可以減慢measure速度。所以在使用之前,一定要再三考慮,是否真的不能通過(guò)其他方法來(lái)完成你要的效果? 官方文檔上 推薦使用RelativeLayout和GridLayout來(lái)避免Layout深度過(guò)深的問(wèn)題 。 之前看文檔,google提供一個(gè)叫 ViewSub 的控件來(lái)優(yōu)化那些不是必須要立即在UI上顯示的控件,感興趣的同學(xué)可以去看看。在API Level 1中就提供了這個(gè)東西,但是在實(shí)際開(kāi)發(fā)中很少見(jiàn)到有人用或者提及(可能是我孤陋寡聞,公司就兩個(gè)Android開(kāi)發(fā),另一個(gè)還要轉(zhuǎn)IOS,我們倆的Android技術(shù)就代表了我們公司的Android技術(shù)能力,想想真悲哀!),其實(shí)蠻好用的。 重用Layout。可以使用<include/> <merge/>將其他布局嵌入到當(dāng)前布局中。 ListView的優(yōu)化:ViewHolder的使用;AsyncTask的使用;針對(duì)ListView當(dāng)前滑動(dòng)狀態(tài),對(duì)圖片數(shù)據(jù)的加載進(jìn)行控制;(ListView在配以AsyncTask加載圖片時(shí)需要注 意圖片的加載完顯示的位置以及圖片的緩存問(wèn)題,具體可以參考Google的 Demo )
10、將大消耗操作交給多個(gè)線程。
11、如果你的應(yīng)用需要發(fā)送Broadcast但是又不希望別的應(yīng)用獲取到,或者你不希望處理別的應(yīng)用發(fā)送的同樣的action,那么請(qǐng)使用LocalBroadcastManager。
該類(lèi)是在Android Support v4中提供的,用來(lái)在同一個(gè)應(yīng)用內(nèi)的不同組件之間發(fā)送Broadcast。好處上面說(shuō)了,可以保證應(yīng)用的私密性。會(huì)比全局廣播有更高的效率,但是官方文檔沒(méi)有說(shuō)明具體數(shù)值。具體使用方法:
LocalBroadcastManager.getInstance(this).registerReceiver( mStateReceiver, mStatusIntentFilter);
LocalBroadcastManager.getInstance(this).sendBroadcast(localBroadcastIntent);
另外Broadcast的注冊(cè)一定要放在Activity的有響應(yīng)的時(shí)候注冊(cè),一般在onResume()注冊(cè),在onPause()的時(shí)候取消注冊(cè)。盡量不要程序走到onDestroy()方法里面才對(duì)Broadcast取消注冊(cè),因?yàn)橛行r(shí)候不會(huì)走到該方法,就會(huì)導(dǎo)致崩潰或者再次進(jìn)入界面是多次注冊(cè)。
12、關(guān)于Fragment使用:
1)無(wú)論在哪里使用getResources()方法,都是需要判斷是否獲取的為null。
2)在使用Fragment+asyntask時(shí)的輸出結(jié)果中最好使用isAdded()方法來(lái)判斷一下,如果為真,那么才進(jìn)行后續(xù)操作。
13、關(guān)于動(dòng)畫(huà)
動(dòng)畫(huà)結(jié)束時(shí),不能修改View的層級(jí)關(guān)系,如果要修改,使用如下方法:
@Override
public void onAnimationEnd(Animation animation) {
new Handler().post(new Runnable() {
public void run() {
myLayout.removeView(mRemoveView);
}
});
}