一.概述
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();
}