效能優化篇---記憶體管理之Android記憶體洩露
- 記憶體洩漏:當你不再需要某個例項後,但是這個物件卻仍然被引用,防止被垃圾回收。這個情況就叫做記憶體洩露(Memory Leak)。
常見洩漏場景:
1.Handler 導致的記憶體洩漏
12345678910111213141516171819202122 |
public class SampleActivity extends AppCompatActivity { private final Handler mLeakyHandler = new Handler() { public void handleMessage(Message msg) |
原理淺析
當 Android 應用程式啟動時,framework 會為該應用程式的主執行緒建立一個 Looper 物件,負責輪詢從Message Queue取資料。
當在主執行緒中例項化一個 Handler 物件後,會自動與主執行緒 Looper 的訊息佇列關聯起來。當 Activity 已經結束/銷燬,而handmerssage有未處理完的訊息時(
123456789101112131415 | private static class MyHandler extends Handler { private final WeakReference<Sample2Activity> mActivity; public MyHandler(Sample2Activity activity) { mActivity = new WeakReference<Sample2Activity>(activity); } public void handleMessage (Message msg) { Sample2Activity activity = mActivity.get(); if (activity != null) { // ... } } } |
- 方案1:靜態內部類加
WeakRefrence。
- 方案2:當退出activity時,要注意所在Handler訊息佇列中的Message是否全部處理完成,可以考慮
removeCallbacksAndMessages(null)
手動關閉
2.靜態變數導致記憶體洩漏
1234567891011121314151617 | public class Sample3Activity extends AppCompatActivity{ private static Context sContext; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sample3); sContext = this; //finish(); Button button = (Button)findViewById(R.id.finish); button.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { finish(); } }); }} |
可能導致記憶體洩漏,原因是:靜態變數持有當前 Activity。導致當前 Activity 結束時候,靜態變數仍然持有它的引用。可以參見[Android靜態變數的生命週期]。建議少引用,或者及時需要手動置空。
3.單利模式導致記憶體洩漏,如果用到Context ,儘量用ApplicationContext
12345678910111213 | public class AppManager { private static AppManager instance; private Context context; private AppManager(Context context) { this.context = context.getApplicationContext(); } public static AppManager getInstance(Context context) { if (instance != null) { instance = new AppManager(context); } return instance; }} |
4.非靜態內部類持有外部類的例項
123456789101112131415 | public class Sample4Activity extends AppCompatActivity { private static LeakSample mLeakSample = null; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(mLeakSample == null){ mLeakSample = new LeakSample(); } //... } class LeakSample { //... }} |
因為非靜態內部類持有外部類物件的引用。正確的做法為: 將該內部類設為靜態內部類或將該內部類抽取出來封裝成一個單例,如果需要使用Context,請使用ApplicationContext。
5.執行緒造成的記憶體洩漏
Runnable 是一個匿名內部類( AsyncTask 存在匿名內部類的情況),對當前 Activity 都有一個隱式引用。例項程式碼如下:
1234567891011121314151617181920212223 | public class Sample4Activity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sample3); leakSample(); finish(); } private void leakSample() { new MyThread().start(); } private class MyThread extends Thread { public void run() { while (true) { SystemClock.sleep(1000); } } } |
當你執行耗時任務,在onDestroy()
的時候考慮呼叫Thread.close()
,如果對執行緒的控制不夠強的話,可以使用RxJava自動建立執行緒池進行控制,並在生命週期結束時取消訂閱;
在使用AsyncTask時,在Activity銷燬時候也應該取消相應的任務AsyncTask.cancel()方法,避免任務在後臺執行浪費資源,進而避免記憶體洩漏的發生。
6.屬性動畫導致記憶體洩漏
屬性動畫中有一類無線迴圈的動畫,要及時取消,否則最終導致 Activity 無法被釋放。動畫的特徵程式碼如下:
1 | animator.setRepeatCount(ValueAnimator.INFINITE); |
解決辦法自然很簡單,在 OnDestory() 中去取消動畫即可.
7.資源未關閉造成的記憶體洩漏
對於使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等資源的使用,應該在Activity銷燬時及時關閉或者登出,否則這些資源將不會被回收,造成記憶體洩漏。
其他 :
廣播洩露:手動註冊廣播時,記住退出的時候要unregisterReceiver()
第三方SDK/開源框架洩露:ShareSDK, JPush等第三方SDK需要按照文件控制生命週期各種callBack/Listener的洩露,要及時設定為Null,特別是static的callback某些Service也要及時關閉,比如圖片上傳,當上傳成功後,要stopself(),建議用intentservice。
Webview需要手動呼叫WebView.onPause()
以及WebView.destory()
多用洩露檢測LeakCanary
開始使用
1.在 build.gradle
中加入引用,不同的編譯使用不同的引用:
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
}
2.在 Application
中:
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}
詳見LeakCanary 中文使用說明
https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
這樣,就萬事俱備了! 在 debug build 中,如果檢測到某個 activity 有記憶體洩露,LeakCanary 就是自動地顯示一個通知。
,或者MAT僅工具。參考:
http://www.jianshu.com/p/c59c199ca9fa