Android中的幾種內存泄露情況總結

Handler使用不當導致的內存泄露(用這個來作爲非靜態內部類造成內存泄露的代表)

  這種情況比較常見,經典的例子如下所示:     

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MainActivity extends AppCompatActivity {

    private ImageView mImageView;

    Handler handler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            if (msg.what == 1) {
                mImageView.setImageBitmap((Bitmap) msg.obj);
            }
        }

    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView = (ImageView) findViewById(R.id.imageView);

        new Thread(new Runnable() {
            @Override
            public void run() {

                Bitmap bitmap = null;
                try {
                    URL url = new URL("http://img0.imgtn.bdimg.com/it/u=3527986333,3942718311&fm=21&gp=0.jpg");
                    URLConnection urlConnection = url.openConnection();
                    InputStream inputStream = urlConnection.getInputStream();
                    bitmap = BitmapFactory.decodeStream(inputStream);
                } catch (IOException e) {
                    e.printStackTrace();
                }

                Message message = new Message();
                message.what = 1;
                message.obj = bitmap;
                handler.sendMessage(message);

            }

        }).start();

    }

}  

  開了個線程從網絡中獲取資源(我這裏下了張圖片),下完了,通過Handler發個消息回UI線程,然後在handleMessage方法中更新UI。當然,犯這種錯誤,Android Studio馬上會提醒,Handler那一塊代碼會有一塊陰影,鼠標光標點在哪,左下角也會出現提示:

This Handler class should be static or leaks might occur (null)  

  處理方式嘛,提示信息也說了,要把Handler應該聲明爲靜態類,否則內存泄露,因爲在Java中非靜態內部類會持有外部類的引用,而那個網絡請求的線程又持有Handler的引用,所以假如此時用戶按了返回鍵,這個Activity佔用的內存本來應該可以被回收的,可是現在卻不行了,於是發生了內存泄露。
  解決方法,僅僅加static是不夠的,因爲這樣Handler類沒有了外部類的引用,那麼也就沒法更新UI了,所以完整的解決方法如下所示:

static class MyHandler extends Handler {

    WeakReference<MainActivity> mWeakReference;

    MyHandler(MainActivity mainActivity) {
        mWeakReference = new WeakReference<>(mainActivity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if (mWeakReference.get() != null) {
            if (msg.what == 1) {
                mWeakReference.get().mImageView.setImageBitmap((Bitmap) msg.obj);
            }
        }

    }

}  

  這樣的話,Handler持有的是MainActivity的弱引用,當Activity被銷燬,GC需要回收內存的時候便可以順利回收。

靜態變量導致的內存泄露

  我們知道在Java中靜態變量的生命週期起始於類的加載,終止於類的釋放(不過好像在哪看到Android中靜態變量好像比較不穩定,不知道什麼時候就會被置空的,有時間去求證下),不過不管怎麼樣,讓一個靜態變量持有Activity引用,肯定不是一個好的選擇。所以可以看到在上面的例子中,雖然我們可以將ImageView設爲static來在static class中進行更新,但我們並沒有這樣做。
  今天先寫到這,下次接着寫。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章