先看下面这段代码

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 否则可能会引起内存泄露的问题

为什么可能会内存泄露呢? 我们来分析一些已有信息

  1. 当一个Android应用第一次启动的时候,系统会创建一个Loop对象在应用的主线程中,Loop就是一个消息队列(Message queue),处理一个又一个的信息。应用的主要事件比如:Activity的生命周期,点击事件等等,都包含在消息(message)对象中。消息会在Loop 的消息队列中排队等着被处理,所以这个主线程的Loop会在应用的整个生命周期中始终存在

  2. 现在我们的handler已经在主线程声明,很自然的他与主线程Loop的消息队列关联了起来。这时传到消息队列中的消息会带有handler的引用,因为当消息被处理时系统需要自己去调用Handler的handleMessage(Message msg)方法

  3. 在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)的隐式引用。把它声明成静态的

更多相关文章

  1. Android消息机制不完全解析(下)
  2. Android消息推送机制调研
  3. Android消息机制之四---Looper,Handler,Message例子
  4. Android中Message机制的灵活应用
  5. Android消息循环机制源码分析
  6. android通过webservice验证用户
  7. Android(安卓)Handler 通信 - 彻底了解 Handler 的通信过程
  8. 基于Paho Android(安卓)Service 实现MQTT协议通信Android客户端
  9. Android笔记--handler机制

随机推荐

  1. android studio 删除、导入jar包
  2. eclipse下 Failed to find an AVD compat
  3. Android对话框
  4. eclipse android 错误列表
  5. android从xml创建控件(按钮)或直接创建控件
  6. Android 更新升级下载 自定义Updates 兼
  7. afinallogoAndroid的快速开…
  8. Android webkit image的加载过程解析(三)
  9. android 显示特殊符号
  10. 第一行代码 Android(安卓)第 2 版 读书笔