android 常见的内存泄漏

来源:互联网 发布:linux打开图形界面命令 编辑:程序博客网 时间:2024/05/21 06:48

好久没写博客了,今天提辞职了,希望以后还能一如既往的继续写博客,今天写下常见的内存泄漏的案例,可能在我们平时开发中经常这么写,但是并没发现有什么内存泄漏问题? 分为如下几种

第一种情况静态变量引起的内存泄露

我们在获取屏幕的宽和高时,都会写一个通用的工具类,方便使用:

package com.leak;import android.content.Context;import android.view.WindowManager;/** * Created by zhouguizhi on 2017/10/23. */public class CommonUtils {    private static CommonUtils INSTANCE = null;    private Context mContext;    private WindowManager windowManager;    public CommonUtils(Context context) {        this.mContext = context;        windowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);    }    public static CommonUtils getInstance(Context context){       if(null==INSTANCE){           INSTANCE = new CommonUtils(context);       }       return INSTANCE;    }    public int getScreenWidth(){        if(windowManager==null){            return -1;        }        return windowManager.getDefaultDisplay().getWidth();    }    public int getScreenHeight (){        if(windowManager==null){            return -1;        }        return windowManager.getDefaultDisplay().getHeight();    }}
在MainActivity调用:
package com.leak;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        CommonUtils.getInstance(this).getScreenHeight();    }}

然后通过Link检查会发现这段代码有啥问题:


解决方法:传Application就行

第二种情况非静态内部类引起内存泄露

package com.leak;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;@SuppressWarnings("unused")public class MainActivity extends AppCompatActivity {    private final int b = 10;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        loadData();    }    private void loadData() {        new Thread(new Runnable() {            @Override            public void run() {                while (true){                    try {                        int a  =b;                        Thread.sleep(3000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        }).start();    }}
这个通过Link既然没检查出来,我就使用了LeakCanary,运行起来发现确实有内存泄漏的问题:


其实问题是出现在这段话:

int a  =b;
因为new Thread()是一个匿名内部类,而a=b,其实是a=MainActivity.this.b,隐士的持有MainActivity实例,这样比如在你调用网络请求还没结束的时候,这个时候你退出这个界面的,但是这个线程还会在运行,这样就导致这个Activity无法销毁,

解决办法:

把loadData()方法改为静态方法即可!因为静态内部类不会隐士持有外部类实例.

这样虽然解决了内存泄漏的问题,但是无法使用外部类的变量了,而你又想使用外部类的变量怎么办?

我改成这样:

package com.leak;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;@SuppressWarnings("unused")public class MainActivity extends AppCompatActivity {    private final int b = 10;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        loadData();    }    private   void loadData() {        MyThread myThread = new MyThread(this);        myThread.start();    }    private class MyThread extends Thread{        private MainActivity activity;        public MyThread(MainActivity activity){            this.activity = activity;        }        @Override        public void run() {            super.run();            while (true){                    try {                       int a = activity.b;                        Thread.sleep(3000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }        }    }}
其实问题还是一样的:


一般是这么解决的:

package com.leak;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import java.lang.ref.WeakReference;@SuppressWarnings("unused")public class MainActivity extends AppCompatActivity {    private final int b = 10;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        loadData();    }    private   void loadData() {        MyThread myThread = new MyThread(this);        myThread.start();    }    private class MyThread extends Thread{        private WeakReference<MainActivity> weakReference;        public MyThread(MainActivity activity){            this.weakReference = new WeakReference<>(activity);        }        @Override        public void run() {            super.run();            while (true){                    try {                        MainActivity activity = weakReference.get();                       int a = activity.b;                        Thread.sleep(3000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }        }    }}
使用软引用或者弱引用来解决的.

第三种情况Handler引起的内存泄漏

package com.leak;import android.os.Handler;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;@SuppressWarnings("unused")public class MainActivity extends AppCompatActivity {    private final int b = 10;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        loadData();    }    private   void loadData() {        new Handler().postDelayed(new Runnable() {            @Override            public void run() {                int a = b;            }        },2000);    }}
平时也有人这么写,之前我干过这种事,

这个和上面第二种情况是一样的,


发现内存泄漏了,那我改成这样:

package com.leak;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;@SuppressWarnings("unused")public class MainActivity extends AppCompatActivity {    private final int b = 10;    private Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            if(msg.what==100){                int a = b;            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Message msg = new Message();        msg.what=100;        mHandler.sendMessageDelayed(msg,20000);    }}
其实还是会出现内存泄漏:因为

mHandler是匿名内部类的实例,会引用外部对象MainActivity.this。如果Handler在MaiinActivity退出的时候,它可能还活着,这时候就会一直持有MainActivity

解决方法:

package com.leak;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import java.lang.ref.WeakReference;@SuppressWarnings("unused")public class MainActivity extends AppCompatActivity {    private final int b = 10;    private static class MyHandler extends Handler{        private WeakReference<MainActivity> weakReference;//设置软引用保存,当内存一发生GC的时候就会回收。        public MyHandler(MainActivity mainActivity) {            this.weakReference = new WeakReference<>(mainActivity);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            MainActivity main =  weakReference.get();            if(main==null||main.isFinishing()){                return;            }            switch (msg.what){                case 100:                    int a = main.b;                    break;            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Message msg = new Message();        msg.what=100;        MyHandler mHandler = new MyHandler(this);        mHandler.sendMessageDelayed(msg,20000);    }}
第四种情况 资源未关闭引起的内存泄露

比如:BroadCastReceiver、Cursor、Bitmap、IO流、自定义属性attribute,attr.recycle()回收。当不需要使用的时候,要记得及时释放资源。否则就会内存泄露

第五种情况无限循环动画引起的内存泄露

没有在onDestroy中停止动画,否则Activity就会变成泄露对象。比如:轮播图效果