1. 程式人生 > >Java,Android記憶體洩漏程式碼片段

Java,Android記憶體洩漏程式碼片段

一個生命週期長的物件引用了一個生命週期短的物件,導致短物件無法被回收

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        /**
         * 這裡需要一個回撥物件,當前Activity實現了回撥介面,並將當前Activity的引用傳遞給網路請求模組
         * 噹噹前Activity被finish掉,但網路請求模組持有的Activity的引用還未釋放(比如網路處理還未完成或者沒有釋放引用),
         * 這時就會造成Activity無法被GC回收,造成記憶體洩漏
         */
        netWorkManager.request(url,this);

        /**
         * 傳遞需要介面的物件就不會導致Activity洩漏了
         */
        netWorkManager.request(url, new NewworkCallBack() {
            @Override
            public void onSuccess() {

            }

            @Override
            public void onFail() {

            }
        });
    }

    @Override
    public void onSuccess() {

    }

    @Override
    public void onFail() {

    }
}

與流有關的溢位,比如流沒有被關閉

public class MemoryLeakDemo extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        /**
         * ref: http://stackoverflow.com/questions/9515465/strictmode-complains-about-inputstream-not-being-closed
         */

        /**
         * 看BufferedInputStream和FileInputStream的原始碼可知,它們的建構函式裡會報異常,FileInputStream或BufferedInputStream物件會生成,
         * 但 objectInputStream仍然為null,在finally程式碼塊中就並不會close FileInputStream和BufferedInputStream物件
         */
        ObjectInputStream objectInputStream;
        File file;
        try {
            file = new File("demo/myfile");
            objectInputStream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)),STREAM_BUFFER_SIZE);
            ...
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            closeQuietly(objectInputStream);
        }

        /**
         * 解決辦法很簡單
         */
        FileInputStream fileInputStream;
        BufferedInputStream bufferedInputStream;
        ObjectInputStream objectInputStream;
        File file;
        try {
            file = new File("demo/myfile");
            fileInputStream = new FileInputStream(file);
            bufferedInputStream = new BufferedInputStream(fileInputStream,STREAM_BUFFER_SIZE);
            objectInputStream = new ObjectInputStream(bufferedInputStream);
            ...
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            closeQuietly(fileInputStream);
            closeQuietly(bufferedInputStream);
            closeQuietly(objectInputStream);
        }
    }

    private void closeQuietly(InputStream is){
        if(is != null){
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

與Context有關的溢位。

對於需要Context的長期存在的物件,要使用ApplicataionContext,而不要使用Activity的Context,否則會導致Activity無法被回收。

使用Activity的Context的物件的生命週期要和Activity一致。

不要在Activity內使用內部類,如果無法控制它的生命週期的話,使用靜態內部類替代,並且用WeakReference引用外部的Activity。因為內部類會拿著外部類的引用,所以內部類有可能導致外部類無法被回收。

public class MemoryLeakDemo extends Activity{
    private static Drawable sBackground;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        /**
         * android應用中,對Context的不適當使用會造成Activity的無法被回收
         * 比如此例中,large_bitmap是個大圖片,這個Activity可能會啟用多次,為了節省載入圖片的時間,將其設成靜態變數
         * 這會提高速度,但會導致Activity無法被回收,因為View會作為Drawable的callback,Drawable拿著View的引用,
         * View又拿著Activity的引用。
         * 解決方法可以是,在Activity退出時將Drawable的callback置為null
         */
        TextView label = new TextView(this);
        label.setText("Leaks are bad");

        if (sBackground == null) {
            sBackground = getDrawable(R.drawable.large_bitmap);
        }
        label.setBackgroundDrawable(sBackground);

        setContentView(label);
    }
}