Android優化一:提綱
Android優化二:性能檢測
Android優化三:內存優化
Android優化四:App啟動速度優化
Android優化五:布局優化
Android優化六:性能優化
什么是內存泄漏?
根據 Java 內存回收機制的“可達性分析法”,如果這些對象是可達的,但是這些對象是無用的,就會導致內存泄漏,內存泄漏的積累最終導致內存溢出。
分類
Android中內存溢出主要分為四類:
①集合類泄漏
②單例/靜態變量造成的內存泄漏
③匿名內部類/非靜態內部類
④資源未關閉造成的內存泄漏
Q:單例為什么會導致內存泄漏?
其實單例本身跟內存泄漏是沒什么關系的,只有在單例使用不恰單才會導致內存泄漏。
單例導致內存泄漏主要的原因是:單例的靜態特性使得單例的生命周期跟整個應用的生命周期一樣長。
如果我們在單例中傳入的 Context 是 Activity 的 context,當這個 Context 所對應的 Activity 退出時,由于該 Context 的引用被單例對象所持有,其生命周期等于整個應用程序的生命周期,所以當前 Activity 退出時它的內存并不會被回收,這就造成泄漏了。
同理被static
修飾的成員變量也是如此,其生命周期將與整個app進程生命周期一樣。
Q:handler 和非靜態內部類為什么會導致內存泄漏?
非靜態內部類默認會持有外部類的引用,handler 的生命周期與 Activity 的生命周期不一致,
如果 Activity 銷毀了但是 Handler 里面有未處理完的延時消息,導致 Activity 不能被 GC 回收。
OOM異常
- 可以通過getMemoryClass( )來獲取App的可用堆內存,如果申請的內存超過這個值,就會造成OOM異常。
- 可以在AndroidManifest.xml文件<applicatiion>中可以設置
android:largeHeap="true"
活的更大的堆內存。
具體細節
1、Bitmap圖片過大
解決辦法:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
圖片寬高都為原來的二分之一,即圖片為原來的四分之一。
bitmap.get().recycle();
在用完Bitmap時,要及時的recycle掉。recycle并不能確定立即就會將Bitmap釋放掉,但是會給虛擬機一個暗示:“該圖片可以釋放了”。軟引用(SoftRefrence)
我們使用Bitmap后沒有保留對它的引用,因此就無法調用Recycle函數。這時候巧妙的運用軟引用,可以使Bitmap在內存快不足時得到有效的釋放 。
但是是現在已經不再推薦使用這種方式了,因為從 Android 2.3 (API Level 9)開始,垃圾回收器會更傾向于回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得不再可靠。另外,Android 3.0 (API Level 11)中,圖片的數據會存儲在本地的內存當中,因而無法用一種可預見的方式將其釋放,這就有潛在的風險造成應用程序的內存溢出并崩潰。
From 郭霖的博客
- 建議使用成熟的Glide、Picasso、Fresco框架來加載圖片。
2、界面切換
- 看頁面布局有沒有大的圖片,比如背景圖之類的。
- 直接把XML配置成view再放到一個容器里面,避免重復加載。
- 在頁面切換時盡可能少地重復使用一些代碼。
3、資源未關閉
-
BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap
等資源的使用沒有注銷導致。 - 在Activity銷毀時及時關閉或者注銷。
4、ListView沒有使用緩存
- 使用的convertView進行緩存
- 建議使用5.0出來的RecycleView替代Listview。
5、Handler導致
- 應該申明為靜態對象, 并在其內部類中保存一個對外部類的弱引用。
- 在Activity銷毀時及時關閉或者注銷
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//handler導致內存泄漏;
//當Activity銷毀時,匿名內部類一直持有Activity的引用,無法釋放。
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
//執行邏輯
}
}, 10000L);
}
6、 線程導致
- 線程產生內存泄露的主要原因在于線程生命周期的不可控。
- 將線程的內部類,改為靜態內部類。
- 采用線程池, 避免程序中存在大量的Thread。
7、盡量使用9path圖片
.9圖片可以任意調整大小,進行拉伸。
8、使用單例造成
- 當調用getInstance時,如果傳入的context是Activity的context。只要這個單例沒有被釋放,那么這個Activity也不會被釋放一直到進程退出才會釋放。
- 使用Application的Context。
9、非靜態內部類創建靜態實例
- 將非靜態內部類修改為靜態內部類。(靜態內部類不會隱式持有外部類)
- Context盡量使用Application Context,因為Application的Context的生命周期比較長。
10、使用了靜態的Activity和View
private static View sView;
- 應該及時將靜態的應用 置為null,而且一般不建議將View及Activity設置為靜態。
11、屬性動畫導致的內存泄漏
- 屬性動畫有一類無限循環的動畫, 如果在Activity中播放此類動畫且沒有在Activity退出的時候沒有停止動畫. 盡管無法界面上看到效果, 但是創建這個動畫所關聯的
View
被動畫所持有, 而View
又持有了Activity
, 最終Activity無法釋放. - 解決方案是在onDestroy()中調用動畫的cancel()來停止動畫.
12、幀動畫導致
- 幀動畫使用的圖片過大過多導致
終極大招:LeakCanary
LeakCanary 是一個開源的在debug版本中檢測內存泄漏的java庫。
LeakCanary 中文使用說明
平時寫代碼稍微注意點,再用這個檢測基本能搞定所有oom異常。