博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Android] The handler class should be static or leaks might occur原因及解决方法
阅读量:5887 次
发布时间:2019-06-19

本文共 3282 字,大约阅读时间需要 10 分钟。

翻译自http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html

 

在主线程中使用Handler对象,比如下面的代码

public class SampleActivity extends Activity {  private final Handler mLeakyHandler = new Handler() {    @Override    public void handleMessage(Message msg) {      // ...     }  }}

这段代码会产生隐秘的内存溢出的错误,Android Lint会给出羡慕的警告:

The handler class should be static or leaks might occur.

但是究竟哪一部分内存会在什么情况下溢出呢?

要解决这一问题需要了解一下安卓系统背景知识:

1. 当一个Android应用启动的时候,Android系统为这个应用的主线程创建一个对象。Looper对象实现了简单的消息队列(),依次处理循环(Looper)中的消息。所有的主要应用事件(比如Activity的生命周期,按钮的点击等)都被包裹为一个消息(Message)实体,被添加到Looper的消息队列中依次处理。主线程的Looper在应用的整个生命周期中都存在。

2.当一个Handler对象在主线程中初始化时,它被关联到Looper的消息队列中。在Handler的sendMessage()方法被调用的时候,一个Message对象会被发往Looper的消息队列中,被发送到消息队列中的Message将会持有Handler的引用,然后系统才能在Looper处理Message时调用Handler对象的handleMessage(Message)方法。

3.在Java中,非静态(non-static)内部和匿名类将会持有外部类的引用。相反,静态的内部类不会持有外部类的引用。

更多Looper,Handler相关的知识可以在文后链接的博客中找到。

了解了这些背景之后我们来看一下内存在什么情况下溢出,首先看下面一段代码:

public class SampleActivity extends Activity {   private final Handler mLeakyHandler = new Handler() {    @Override    public void handleMessage(Message msg) {      // ...    }  }   @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);     // Post a message and delay its execution for 10 minutes.    mLeakyHandler.postDelayed(new Runnable() {      @Override      public void run() { /* ... */ }    }, 1000 * 60 * 10);     // Go back to the previous Activity.    finish();  }}

当Activity结束的时候,被延迟的Message会继续存活在主线程中10分钟,直到它被处理了。这个Message持有Activity的Handler对象的引用,同时Handler是Activity的非静态(non-static)匿名内部类,所以Handler持有外部类也就是Activity的引用。这样的引用关系会阻止Activity被Java GC回收,释放系统持有的资源,直到Message被处理了。另外杨的引用对于匿名Runnable()对象也存在。

解决这个问题的方法可以有:在一个新的文件中继承Handler类,或者使用一个静态内部类。静态内部类不会持有外部类的引用,所以Activity不会被泄露。如果需要在Handler中调用外部类(此处为Activiity)的方法,可以使Handler拥有Activity的弱引用(WeakReference),这样就不会意外地泄露Activity。

为了解决实例化内部匿名Runnable类时导致的内存泄漏的问题,我们可以使内部匿名Runnable类变为外部类的静态对象。

public class SampleActivity extends Activity {  /**   * Instances of static inner classes do not hold an implicit   * reference to their outer class.   */  private static class MyHandler extends Handler {    private final WeakReference
mActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference
(activity); } @Override public void handleMessage(Message msg) { SampleActivity activity = mActivity.get(); if (activity != null) { // ... } } } private final MyHandler mHandler = new MyHandler(this); /** * Instances of anonymous classes do not hold an implicit * reference to their outer class when they are "static". */ private static final Runnable sRunnable = new Runnable() { @Override public void run() { /* ... */ } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mHandler.postDelayed(sRunnable, 1000 * 60 * 10); // Go back to the previous Activity. finish(); }}

 

在Activity中实例化内部类时,如果内部类可以在Activity的生命周期之外继续存活于哦,那么这样的内部类不能为非静态的。这种情况应该使用静态内部来并且持有外部类对象的弱引用。

 

 

相关链接:

http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html

转载于:https://www.cnblogs.com/zoejiaen/p/4580572.html

你可能感兴趣的文章
vue进行wepack打包执行npm run build出现错误
查看>>
【d3.js v4基础】过渡transition
查看>>
VUEJS开发规范
查看>>
Android系统的创世之初以及Activity的生命周期
查看>>
人人都会数据采集- Scrapy 爬虫框架入门
查看>>
Android网络编程11之源码解析Retrofit
查看>>
韩国SK电讯宣布成功研发量子中继器
查看>>
TCP - WAIT状态及其对繁忙的服务器的影响
查看>>
安全预警:全球13.5亿的ARRIS有线调制解调器可被远程攻击
查看>>
麦子学院与阿里云战略合作 在线教育领军者技术实力被认可
查看>>
正确看待大数据
查看>>
Facebook通过10亿单词构建有效的神经网络语言模型
查看>>
2016股市投资风向标 大数据说了算
查看>>
发展大数据不能抛弃“小数据”
查看>>
中了WannaCry病毒的电脑几乎都是Win 7
查看>>
学生机房虚拟化(九)系统操作设计思路
查看>>
nginx报错pread() returned only 0 bytes instead of 4091的分析
查看>>
质数因子
查看>>
Spring源码浅析之事务(四)
查看>>
[转载] Live Writer 配置写 CSDN、BlogBus、cnBlogs、163、sina 博客
查看>>