Android 常见的几种内存泄漏

来源:互联网 发布:怎样注册一个域名 编辑:程序博客网 时间:2024/05/22 18:23

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的内存资源无法及时回收,引发内存泄漏。

那怎么样才可以避免这中问题呢,这里提供两个解决方案。这个问题是由于当Activity被销毁时发现引用它的Handler还在,从而导致了GC无法回收Activity。那么最简单最直接的方法是在Activity的OnDestory方法中移除所有的消息和回调即清空队列,MessageQueue被释放 从而使Handler得到释放,这时GC就能正常回收被销毁的Activity了。所以这种解决方案方法如下:

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

这个解决方案我们忽略了消息队列的作用。由于Handler对于Activity的引用导致GC无法回收Activity,我们直接暴力的把消息队列清空了,那如果需要这些处理消息队列中的消息呢?这就需要用到我们第二个解决方案:弱引用。其原理就是让所有在handler里面使用的对象都变成弱引用,目的就是为了可以在Android回收内存的时候,可以直接回收掉。

public class MainActivity extends AppCompatActivity {    private MyHandler mHandler = new MyHandler(this);    private TextView mTextView ;    private static class MyHandler extends Handler {        private WeakReference<Context> 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);    }}