在Android開發開發中,操作不當很容易引起內存泄漏,這裏主要記錄下平時遇到問題,包括:靜態變量(也包含集合)、非靜態的內部類、Handler、監聽器,尤其是 Handler 在開發中要格外的注意。
同步發佈在簡書Android 內存泄漏案例分析總結(Handler)
1、靜態變量
public class LeakActivityDemo extends Activity{
private static TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = new TextView(this);
mTextView.setText("demo");
}
}
我這裏這是一個例子,一般開發不會這麼寫,上面的代碼在 Activity 結束的時候,mTextView 一直持有 this 引用,
導致整個 Activity 無法回收
解決:方法在 Activity 生命週期 onDestroy 時將 mTextView 置空,或者儘量少使用到靜態變量。
注意:在寫代碼時,要考慮到當前的變量是否持有當前 Activity 的引用,避免出現內存泄漏.
2、非靜態內部類
關於 Handler
public class LeakActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 1);
}
//提示:This Handler class should be static or leaks might occur (com.mvp.view.LeakActivity.1)
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
在 Android 開發中,我們一般使用 Handler 處理異步操作,通常我們的代碼會像上面一樣實現,但是上面的代碼可能存在泄漏,其實編譯器已經提示了警告,建議使用 static 靜態標示。
原因:
1、首先在 java 中,非靜態內部類會或者內部類會持有外部對象的引用,而靜態內部類不會持有外部類的引用;
2、和 Handler 機制有關,我們知道 Handler 和 Looper 是一起工作的,在一個 Activity 起來的時候,會幫我們創建一個
在主線程中使用的 Looper,用來處理所有的消息,當 Hanlder 初始化好以後,就會有一個消息發送到了 Looper 的消息隊列中,
而這個消息則包含了當前 Handler 的引用,這就是內存泄露的原因;
解決:
1、使用靜態內部類,如果在內部類中使用了 Activiy 則要使用 WeakReference(弱引用),並且需要注意判空。
2、還有Hanlder要在生命週期 ondestroy 時,取消該 Handler 對象的 Messag 和 Runnable。
例如:
removeCallbacks(Runnable r)、removeMessages(int what)、mHandler.removeCallbacksAndMessages(null);
private final MyHandler mHandler = new MyHandler(this);
public static class MyHandler extends Handler {
private final WeakReference<LeakActivity> mWeakActivity;
public MyHandler(LeakActivity context) {
mWeakActivity = new WeakReference<LeakActivity>(context);
}
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mWeakActivity.get() != null) {
mWeakActivity.get().todo();
}
}
}
public void todo(){
//todo
}
關於 Runnable
常常會這麼寫,會存在很大的內存泄露問題,內部類會持有外部對象的引用,如果我們 run 方法操作了 UI 等,或者 使用了 postDelayed 方法,很容易造成內存泄露問題。
mHandler.post(new Runnable() {
@Override
public void run() {
..
}
});
解決方法:使用靜態的Runnable 和 WeakReference 來解決引用問題,使用 WeakReference 要注意判斷,因爲可能隨時會被回收。
private static final class ItemRunnble implements Runnable {
private final WeakReference<Item> mWeakReference;
public ProgressBarRunnble(Item f) {
mWeakReference = new WeakReference<Item>(f);
}
@Override
public void run() {
Item item = mWeakReference.get();
if (item != null) {
...
}
}
}
mHandler.post(new ItemRunnble(mItem))
3、非靜態內部類生成的靜態變量
private static MyClass myClass;
private class MyClass {
}
..
myClass = new MyClass();
這種是兩者的綜合體,只要是非靜態內部類就會持有外部類的引用,如果外部類正好是 Activity ,那麼會導致 Activity 無法回收;
處理方式和第一種很像,記得釋放
總結
靜態變量最容易出現內存泄露,這些靜態變量的生命週期和應用程序一致,所有的對象 Object 也不能被釋放,
而集合類如果不 remove 添加的 Object,則會永遠持有這些 Ojbect,也會導致無法釋放。
監聽器是一種使用方式,Android 中比較常見的是 Listener,Observer 等,
一般被監聽者的生命週期要比監聽者的生命週期長,當監聽者本身不被使用,但又沒有移除被監聽者對其的引用時就會造成內存無法釋放。
數據庫在不使用時也沒有關閉,那麼這部分內存也就無法回收。
以下列舉注意情況:
- 遵守生命週期,創建時創建,銷燬時記得回收
- Bitmip 和 Drawable 記得手動回收
- 減少靜態變量的使用
- 自定義靜態 Handler,和 Handler 回收
- 使用 Application Context,少使用 Activity Context
- 監聽器不使用的時候記得釋放
- 如果循環中使用較多臨時變量,當不使用時及時釋放