Android handler的Context内存泄露
先看下面这段代码
public class LeakActivity extends Activity { public Handler leakHandler = new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); } };
这里简单的声明了一个handler,看起来一切正常,但是这种写法有可能会引起很大的内存泄露,原因其实编译器已经告诉你了,因为这种写法往往有一个警告:
在Android中 Handler应该被声明为 static 否则可能会引起内存泄露的问题
为什么可能会内存泄露呢? 我们来分析一些已有信息
当一个Android应用第一次启动的时候,系统会创建一个Loop对象在应用的主线程中,Loop就是一个消息队列(Message queue),处理一个又一个的信息。应用的主要事件比如:Activity的生命周期,点击事件等等,都包含在消息(message)对象中。消息会在Loop 的消息队列中排队等着被处理,所以这个主线程的Loop会在应用的整个生命周期中始终存在
现在我们的handler已经在主线程声明,很自然的他与主线程Loop的消息队列关联了起来。这时传到消息队列中的消息会带有handler的引用,因为当消息被处理时系统需要自己去调用Handler的
handleMessage(Message msg)
方法在java中 非静态的内部匿名类(也就是我们声明Handle的方式),会持有他的外部类的一个隐式的应用,当然静态的内部类是不会的。
基于以上几点修改一下这个例子
public class LeakActivity extends Activity { public Handler leakHandler = new Handler() { @Override public void handleMessage(Message msg) { ... } }; protected void onCreate(android.os.Bundle savedInstanceState) { setContentView(R.layout.activity_leak); //10分钟之后发送消息 leakHandler.postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }, 1000 * 60 * 10); //退出到上一个Activity finish(); };
声明的handler延时10分钟发送信息,同时finish()掉当前的Activity,根据上面说明的第二点,延时10分钟发送的信息,依然会在主线程的消息队列中并且等待10分钟后发送信息。那么问题来了由于消息持有handler的引用,而handler又是一个匿名内部类,匿名内部类会持有他的外部类(也就是LeakActivity)的一个隐式引用,而LeakActivity已经被finish()掉了,所以 就有可能会导致Context的内存泄露,注意一下上面的runnable也是一个匿名内部类
如何解决这个隐患呢,方法相信大家已经知道了,使用静态内部类。这样就不会去持有外部的隐式引用。
public class LeakActivity extends Activity { /** * 声明静态内部类不会持有外部类的隐式引用 */ private static class MyHandler extends Handler { private final WeakReference<SampleActivity> mActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference<SampleActivity>(activity); } @Override public void handleMessage(Message msg) { SampleActivity activity = mActivity.get(); if (activity != null) { // ... } } } private final MyHandler mHandler = new MyHandler(this); /** * 这里的Runnable也是 * 声明静态内部类不会持有外部类的隐式引用 */ private static final Runnable sRunnable = new Runnable() { @Override public void run() { /* ... */ } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //10分钟之后发送消息 mHandler.postDelayed(sRunnable, 1000 * 60 * 10); // 退回到上一个Activity finish(); }}
将handler和runnable都声明为静态内部类,同时让handler持有的Context声明为WeakReference 。
FBI Warning
如果内部类可以存在于activity的生命周期之外,那么不要去使用非静态的内部类,因为他会持有外部类(activity)的隐式引用。把它声明成静态的
更多相关文章
- Android消息推送机制调研
- Android消息循环机制源码分析
- Android消息机制之四---Looper,Handler,Message例子
- Android 消息机制 进程调度问题
- UML详解:解析Android消息处理机制:Handler/Thread/Looper & Messag
- Android仿QQ消息列表ListView滑动删除效果
- Android EventBus 传递消息
- [Android] 监听系统网络连接打开或者关闭的消息