Android App 内存泄露之Handler

来源:互联网 发布:吴昕开的淘宝店叫什么 编辑:程序博客网 时间:2024/05/16 10:45

一.概述

Handler是我们开发中经常用到的一个类,其实Handler是造成内存泄漏的一个重要源头,主要就是因为和Activity的生命周期不一致导致的,Hanlder引用Activity会造成内存泄漏。

看一下如下代码

这里写图片描述

开发工具提示我们,这个Hanlder类应该声明为静态的,否则可能发生内存泄漏。
为什么会出现这样的问题?

Handler的生命周期和Activity不一致

  • 当Android应用启动的时候,会先创建一个UI主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。
  • 当在主线程中初始化Handler时,该Handler和Looper的消息队列关联(没有关联会报错的)。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。

    Handler引用Activity阻止了GC对Activity的回收

  • 在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。

  • 如果外部类是Activity,则会引起Activity泄露 。
    当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。

如何避免
1.使用静态内部类
2.使用弱引用

代码如下:

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //创建Handler实例的时候把外部类的对象传进去        MyHandler handler = new MyHandler(this);        handler.sendEmptyMessageDelayed(0,200);    }    //将Handler类声明为静态内部类    public static class MyHandler extends Handler{        public WeakReference<MainActivity> mActivity ;        public MyHandler(MainActivity activity){            //使用弱引用,保存外部类Activity的对象            mActivity = new WeakReference<MainActivity>(activity);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            if(mActivity.get()==null){                return;            }            //调用Activity中的方法            mActivity.get().todo();        }    }    public void todo(){        Toast.makeText(this, "hello", Toast.LENGTH_SHORT).show();    }}

上面这样就可以了吗?

当Activity finish的时候,消息对象还是在消息队列中排队,还是会处理消息,这是没必要的,所以最好的方法是在Activity onStop或者onDestory的时候取消掉该Handler对象的Message和Runnable。

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