Android優(yōu)化一:提綱
Android優(yōu)化二:性能檢測
Android優(yōu)化三:內(nèi)存優(yōu)化
Android優(yōu)化四:App啟動速度優(yōu)化
Android優(yōu)化五:布局優(yōu)化
Android優(yōu)化六:性能優(yōu)化
什么是內(nèi)存泄漏拘鞋?
根據(jù) Java 內(nèi)存回收機制的“可達性分析法”含思,如果這些對象是可達的窃诉,但是這些對象是無用的被饿,就會導致內(nèi)存泄漏塞琼,內(nèi)存泄漏的積累最終導致內(nèi)存溢出隐砸。
分類
Android中內(nèi)存溢出主要分為四類:
①集合類泄漏
②單例/靜態(tài)變量造成的內(nèi)存泄漏
③匿名內(nèi)部類/非靜態(tài)內(nèi)部類
④資源未關(guān)閉造成的內(nèi)存泄漏
Q:單例為什么會導致內(nèi)存泄漏祝沸?
其實單例本身跟內(nèi)存泄漏是沒什么關(guān)系的耘分,只有在單例使用不恰單才會導致內(nèi)存泄漏。
單例導致內(nèi)存泄漏主要的原因是:單例的靜態(tài)特性使得單例的生命周期跟整個應(yīng)用的生命周期一樣長壹店。
如果我們在單例中傳入的 Context 是 Activity 的 context猜丹,當這個 Context 所對應(yīng)的 Activity 退出時,由于該 Context 的引用被單例對象所持有硅卢,其生命周期等于整個應(yīng)用程序的生命周期射窒,所以當前 Activity 退出時它的內(nèi)存并不會被回收,這就造成泄漏了将塑。
同理被static
修飾的成員變量也是如此脉顿,其生命周期將與整個app進程生命周期一樣。
Q:handler 和非靜態(tài)內(nèi)部類為什么會導致內(nèi)存泄漏点寥?
非靜態(tài)內(nèi)部類默認會持有外部類的引用艾疟,handler 的生命周期與 Activity 的生命周期不一致,
如果 Activity 銷毀了但是 Handler 里面有未處理完的延時消息敢辩,導致 Activity 不能被 GC 回收蔽莱。
OOM異常
- 可以通過getMemoryClass( )來獲取App的可用堆內(nèi)存,如果申請的內(nèi)存超過這個值戚长,就會造成OOM異常盗冷。
- 可以在AndroidManifest.xml文件<applicatiion>中可以設(shè)置
android:largeHeap="true"
活的更大的堆內(nèi)存。
具體細節(jié)
1同廉、Bitmap圖片過大
解決辦法:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
圖片寬高都為原來的二分之一仪糖,即圖片為原來的四分之一。
bitmap.get().recycle();
在用完Bitmap時迫肖,要及時的recycle掉锅劝。recycle并不能確定立即就會將Bitmap釋放掉,但是會給虛擬機一個暗示:“該圖片可以釋放了”蟆湖。軟引用(SoftRefrence)
我們使用Bitmap后沒有保留對它的引用故爵,因此就無法調(diào)用Recycle函數(shù)。這時候巧妙的運用軟引用帐姻,可以使Bitmap在內(nèi)存快不足時得到有效的釋放 稠集。
但是是現(xiàn)在已經(jīng)不再推薦使用這種方式了奶段,因為從 Android 2.3 (API Level 9)開始,垃圾回收器會更傾向于回收持有軟引用或弱引用的對象剥纷,這讓軟引用和弱引用變得不再可靠痹籍。另外,Android 3.0 (API Level 11)中晦鞋,圖片的數(shù)據(jù)會存儲在本地的內(nèi)存當中蹲缠,因而無法用一種可預見的方式將其釋放锭亏,這就有潛在的風險造成應(yīng)用程序的內(nèi)存溢出并崩潰辙培。
From 郭霖的博客
- 建議使用成熟的Glide、Picasso属划、Fresco框架來加載圖片确买。
2斤讥、界面切換
- 看頁面布局有沒有大的圖片,比如背景圖之類的湾趾。
- 直接把XML配置成view再放到一個容器里面芭商,避免重復加載。
- 在頁面切換時盡可能少地重復使用一些代碼搀缠。
3铛楣、資源未關(guān)閉
-
BraodcastReceiver,ContentObserver艺普,F(xiàn)ile簸州,Cursor,Stream歧譬,Bitmap
等資源的使用沒有注銷導致岸浑。 - 在Activity銷毀時及時關(guān)閉或者注銷。
4缴罗、ListView沒有使用緩存
- 使用的convertView進行緩存
- 建議使用5.0出來的RecycleView替代Listview助琐。
5祭埂、Handler導致
- 應(yīng)該申明為靜態(tài)對象面氓, 并在其內(nèi)部類中保存一個對外部類的弱引用。
- 在Activity銷毀時及時關(guān)閉或者注銷
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//handler導致內(nèi)存泄漏;
//當Activity銷毀時蛆橡,匿名內(nèi)部類一直持有Activity的引用舌界,無法釋放。
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
//執(zhí)行邏輯
}
}, 10000L);
}
6泰演、 線程導致
- 線程產(chǎn)生內(nèi)存泄露的主要原因在于線程生命周期的不可控呻拌。
- 將線程的內(nèi)部類,改為靜態(tài)內(nèi)部類睦焕。
- 采用線程池, 避免程序中存在大量的Thread藐握。
7靴拱、盡量使用9path圖片
.9圖片可以任意調(diào)整大小,進行拉伸猾普。
8袜炕、使用單例造成
- 當調(diào)用getInstance時,如果傳入的context是Activity的context初家。只要這個單例沒有被釋放偎窘,那么這個Activity也不會被釋放一直到進程退出才會釋放。
- 使用Application的Context溜在。
9陌知、非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實例
- 將非靜態(tài)內(nèi)部類修改為靜態(tài)內(nèi)部類。(靜態(tài)內(nèi)部類不會隱式持有外部類)
- Context盡量使用Application Context掖肋,因為Application的Context的生命周期比較長仆葡。
10、使用了靜態(tài)的Activity和View
private static View sView;
- 應(yīng)該及時將靜態(tài)的應(yīng)用 置為null志笼,而且一般不建議將View及Activity設(shè)置為靜態(tài)浙芙。
11、屬性動畫導致的內(nèi)存泄漏
- 屬性動畫有一類無限循環(huán)的動畫, 如果在Activity中播放此類動畫且沒有在Activity退出的時候沒有停止動畫. 盡管無法界面上看到效果, 但是創(chuàng)建這個動畫所關(guān)聯(lián)的
View
被動畫所持有, 而View
又持有了Activity
, 最終Activity無法釋放. - 解決方案是在onDestroy()中調(diào)用動畫的cancel()來停止動畫.
12籽腕、幀動畫導致
- 幀動畫使用的圖片過大過多導致
終極大招:LeakCanary
LeakCanary 是一個開源的在debug版本中檢測內(nèi)存泄漏的java庫嗡呼。
LeakCanary 中文使用說明
平時寫代碼稍微注意點,再用這個檢測基本能搞定所有oom異常皇耗。