安卓常见的内存泄漏

先解释下内存泄露和内存溢出,这两个比较容易混淆的概念:

内存泄露:程序在向系统申请分配内存空间后(new),在使用完毕后未释放。结果导致一直占据该内存单元,我们和程序都无法再使用该内存单元,直到程序结束,这是内存泄露。比如当一个对象已经不需要再使用了,本该被回收时,而另外一个正在使用的对象持有它的引用从而导致它不能被回收,这就导致本该被回收的对象不能被回收而停留在堆内存中,内存泄漏就产生了。

内存溢出:程序向系统申请的内存空间超出了系统能给的。比如加载一张大图片时候,需要的内存超过系统分配的最大内存,导致程序崩溃;

大量的内存泄露会导致内存溢出(oom)。

1 observer 我们在使用监听器的时候,往往是addxxxlistener,但是当我们不需要的时候,忘记removexxxlistener,就容易内存leak。

广播没有unregisterrecevier

2.对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏

3.Handler造成的内存泄漏

Handler的使用造成的内存泄漏问题应该说最为常见了,平时在处理网络任务或者封装一些请求回调等api都应该会借助Handler来处理,对于Handler的使用代码编写一不规范即有可能造成内存泄漏,如下示例:

  public class MainActivity extends AppCompatActivity {

        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {

            }
        };

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            loadData();
        }
        private void loadData() {
          //...request
            Message message = Message.obtain();
            mHandler.sendMessage(message);
        }
    }

这种创建Handler的方式会造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏,所以另外一种做法为:

public class MainActivity extends AppCompatActivity {

        private MyHandler mHandler = new MyHandler(this);
        private TextView mTextView;
        private static class MyHandler extends Handler {

            private WeakReference reference;
            public MyHandler(Context context) {
                reference = new WeakReference<>(context);
            }

            @Override
            public void handleMessage(Message msg) {
                MainActivity activity = (MainActivity) reference.get();
                if (activity != null) {
                    activity.mTextView.setText("");
                }
            }
        }
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = (TextView) findViewById(R.id.textview);
            loadData();
        }
        private void loadData() {
            //...request
            Message message = Message.obtain();
            mHandler.sendMessage(message);

        }
    }

创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息,更准确的做法如下:

 public class MainActivity extends AppCompatActivity {

        private MyHandler mHandler = new MyHandler(this);

        private TextView mTextView;

        private static class MyHandler extends Handler {

            private WeakReference reference;
            public MyHandler(Context context) {

                reference = new WeakReference<>(context);
            }

            @Override
            public void handleMessage(Message msg) {
                MainActivity activity = (MainActivity) reference.get();
                if (activity != null) {
                    activity.mTextView.setText("");
                }
            }
        }

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = (TextView) findViewById(R.id.textview);
            loadData();
        }

        private void loadData() {
            //...request
            Message message = Message.obtain();
            mHandler.sendMessage(message);
        }

        @Override
        protected void onDestroy() {
            super.onDestroy();
            mHandler.removeCallbacksAndMessages(null);
        }

    }

使用mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有消息和所有的Runnable。当然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();来移除指定的Runnable和Message。

4.单例

因为单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。

5.非静态内部类创建静态实例造成的内存泄漏
why?
非静态的内部类默认会持有外部类的引用
而我们又使用非静态内部类创建了一个静态的实例
该静态实例的声明周期和应用一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity不能正常回收

解决方案:
将内部类修改成静态的,这样它对外部类就没有引用
将该对象抽取出来封装成一个单利.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值