Android(安卓)Handler和内部类的正确用法
引言
在Android中如果错误的使用Handler也会引起内存泄漏的。所以在我们实际开发中还是需要多多注意,并尽量去避免它。
原例
Android代码中涉及线程通信的地方,我们基本都会选择使用Handler。比如:
public class HandlerActivity extends Activity{//可能引起内存泄漏的方法private final Handler mHandler = new Handler(){@Overridepublic void handlerMessage(Message msg){//...}}}
如果使用Android Lint分析这段代码的话,会发现提示:
This Handler class should be static or leaks might occur.
这就是在提醒开发者,这里的写法有可能会引起内存泄漏,需要开发者去处理。那为什么会可能产生内存泄漏呢?这个要从Handler的机制来说,我们都知道Handler是和Looper以及MessageQueue一起使用的。在Android中一个应用启动后,系统会默认创建一个为主线程服务的Looper对象,该对象用于处理主线程的所有Message对象,它的生命周期贯穿于整个应用的生命周期。在主线程中使用的Handler都会默认绑定到这个Looper对象。在主线程中创建Handler对象时,它会立即关联主线程Looper对象的MessageQueue,这时发送到MessageQueue中的Message对象都会持有这个Handler对象的引用,这样在Looper处理消息时才能回调到Handler的handleMessage方法。因此如果Message还没有被处理完成,那么Handler也就不会被垃圾回收。
在上段代码中,我们将Handler的实例声明为了HandlerActivity的内部类。而在Java中非静态内部匿名类会持有外部类的一个隐式的引用,这样就又能能会导致外部类无法被垃圾回收。因此,最终由于MessageQueue中的Message对象还没有处理完成,就会持有Handler对象的引用,而非静态的Handler对象会持有外部类HandlerActivity的引用,这个Activity无法被垃圾回收,从而导致了内存泄漏。
比如下面这种写法:
public class HandlerActivity extends Activity{//可能引起内存泄漏的方法private final Handler mHandler = new Handler(){@Overridepublic void handlerMessage(Message msg){//...}}@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);//延迟5分钟发送消息mHandler.postDelayed(new Runnable(){@Overridepublic void run(){...}},1000*60*5)}}
由于消息要延后5分钟发送,因此,当用户进入这个Activity并在5分钟内退出后,在消息发送并处理完成之前,这个Activity是不会被系统回收的。
那么要怎么解决呢?有两个方案:
- 在子线程中使用Handler,这时需要开发者自己创建一个Looper对象,这个Looper对象的生命周期同一般的Java对象,因此这种用法没有问题。
- 将Handler声明为静态的内部类,前面说过,静态内部类不会持有外部类的引用,因此,也不会引起内存泄漏。
正确用法
所以,下面我们看下经典的用法:
public class HandlerActivity extends Activity{//声明一个静态的Handler内部类,并持有外部类的弱引用private static class InnerHandler extends Handler{private final WeakReference mActivity;public InnerHandler(HandlerActivity activity){mActivity = new WeakReference(activity);}@Overridepublic void handleMessage(Message msg){HandlerActivity activity = mActivity.get();if(activity != null){//...}}}private final InnerHandler mHandler = new InnerHandler(this);//静态的匿名内部类不会持有外部类的引用private static final Runnable sRunnable = new Runnable(){@Overridepublic void run(){//...}}@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);//延迟5分钟发送消息mHandler.postDelayed(sRunnable,1000*60*5);}}
更多相关文章
- android launcher的结构
- Android系列之Intent传递对象的几种实例方法
- [转]五大布局对象---FrameLayout,LinearLayout ,AbsoluteLayout,
- [置顶] android 耳机按钮深层理解
- Android(安卓)从原型模式看java实例化对象clone和new的区别
- android 内存优化详解
- android中常见的内存溢出和解决办法
- 关于Android中使用Handler造成内存泄露的分析和解决
- Android(安卓)判定网络连接状态 以及监听网络链接状态的变化