思考下面代码

1 public class SampleActivity extends Activity {
2
3   private final Handler mLeakyHandler = new Handler() {
4     @Override
5     public void handleMessage(Message msg) {
6       // ...
7     }
8   }
9 }

如果没有仔细观察,上面的代码可能导致严重的内存泄露。Android Lint会给出下面的警告:

In Android, Handler classes should be static or leaks might occur.

但是到底是泄漏,如何发生的?让我们确定问题的根源,先写下我们所知道的
1、当一个Android应用程序第一次启动时,Android框架为应用程序的主线程创建一个Looper对象。一个Looper实现了一个简单的消息队列,在一个循环中处理Message对象。所有主要的应用程序框架事件(如活动生命周期方法调用,单击按钮,等等)都包含在Message对象,它被添加到Looper的消息队列然后一个个被处理。主线程的Looper在应用程序的整个生命周期中存在。
2、当一个Handle在主线程被实例化,它就被关联到Looper的消息队列。被发送到消息队列的消息会持有一个Handler的引用,以便Android框架可以在Looper最终处理这个消息的时候,调用Handler#handleMessage(Message)。
3、在Java中,非静态的内部类和匿名类会隐式地持有一个他们外部类的引用。静态内部类则不会。

那么,到底是内存泄漏?好像很难懂,让我们以下面的代码作为一个例子

 1 public class SampleActivity extends Activity {
 2
 3   private final Handler mLeakyHandler = new Handler() {
 4     @Override
 5     public void handleMessage(Message msg) {
 6       // ...
 7     }
 8   }
 9
10   @Override
11   protected void onCreate(Bundle savedInstanceState) {
12     super.onCreate(savedInstanceState);
13
14     // 延时10分钟发送一个消息
15     mLeakyHandler.postDelayed(new Runnable() {
16       @Override
17       public void run() { }
18     }, 60 * 10 * 1000);
19
20     // 返回前一个Activity
21     finish();
22   }
23 }

当这个Activity被finished后,延时发送的消息会继续在主线程的消息队列中存活10分钟,直到他们被处理。这个消息持有这个Activity的Handler引用,这个Handler有隐式地持有他的外部类(在这个例子中是SampleActivity)。直到消息被处理前,这个引用都不会被释放。因此Activity不会被垃圾回收机制回收,泄露他所持有的应用程序资源。注意,第15行的匿名Runnable类也一样。匿名类的非静态实例持有一个隐式的外部类引用,因此context将被泄露。

为了解决这个问题,Handler的子类应该定义在一个新文件中或使用静态内部类。静态内部类不会隐式持有外部类的引用。所以不会导致它的Activity泄露。如果你需要在Handle内部调用外部Activity的方法,那么让Handler持有一个Activity的弱引用(WeakReference)以便你不会意外导致context泄露。为了解决我们实例化匿名Runnable类可能导致的内存泄露,我们将用一个静态变量来引用他(因为匿名类的静态实例不会隐式持有他们外部类的引用)。

 1 public class SampleActivity extends Activity {
 2     /**
 3     * 匿名类的静态实例不会隐式持有他们外部类的引用
 4     */
 5     private static final Runnable sRunnable = new Runnable() {
 6             @Override
 7             public void run() {
 8             }
 9         };
10
11     private final MyHandler mHandler = new MyHandler(this);
12
13     @Override
14     protected void onCreate(Bundle savedInstanceState) {
15         super.onCreate(savedInstanceState);
16
17         // 延时10分钟发送一个消息.
18         mHandler.postDelayed(sRunnable, 60 * 10 * 1000);
19
20         // 返回前一个Activity
21         finish();
22     }
23
24     /**
25     * 静态内部类的实例不会隐式持有他们外部类的引用。
26     */
27     private static class MyHandler extends Handler {
28         private final WeakReference<SampleActivity> mActivity;
29
30         public MyHandler(SampleActivity activity) {
31             mActivity = new WeakReference<SampleActivity>(activity);
32         }
33
34         @Override
35         public void handleMessage(Message msg) {
36             SampleActivity activity = mActivity.get();
37
38             if (activity != null) {
39                 // ...
40             }
41         }
42     }
43 }

静态和非静态内部类的区别是比较难懂的,但每一个Android开发人员都应该了解。开发中不能碰的雷区是什么?不在一个Activity中使用非静态内部类, 以防它的生命周期比Activity长。相反,尽量使用持有Activity弱引用的静态内部类。

译文链接

转载于:https://www.cnblogs.com/kissazi2/p/4121852.html

【译】什么导致了Context泄露:Handler内部类相关推荐

  1. Java内存泄露系列--内部类持有外部类导致内存泄露--原因/解决方案

    原文网址:Java内存泄露系列--内部类持有外部类导致内存泄露--原因/解决方案_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Java内部类持有外部类导致内存泄露的原因以及其解决方案. 为什么内部 ...

  2. .NET 6 在 Win7 系统证书链错误导致 HttpWebRequest 内存泄露

    本文记录我将应用迁移到 dotnet 6 之后,在 Win7 系统上,因为使用 HttpWebRequest 访问一个本地服务,此本地服务开启 https 且证书链在此 Win7 系统上错误,导致应用 ...

  3. MongoDB数据库因安全漏洞,导致Family Locator泄露二十多万名用户数据

    摘要:本月第二次,未受保护的MongoDB数据库因大量安全漏洞而导致敏感信息泄露,受欢迎的家庭跟踪应用程序Family Locator已经暴露了超过238,000名用户的实时未加密位置数据. 该应用程 ...

  4. 网安入门须知:注释的危害居然这么大?——注释漏洞导致的信息泄露

    隔壁大娘收到了一条匿名短信,里面记录了大娘跟隔壁老王的开房记录,并勒索二百五十块巨款.大娘略加思索后,便提着刀冲到狗剩家门口,一刀砍在门口的卷帘门上 隔壁大娘:狗剩,你给我出来!!! 注释导致的信息泄 ...

  5. 美征信巨头Equifax因Struts漏洞导致数据大规模泄露

    各新闻机构和在线新闻网站都报道了黑客从征信企业Equifax窃取了1.43亿美国人的详细个人信息,这一事件表明Apache Struts框架存在安全缺陷.Struts是一种开源的MVC框架,用于创建基 ...

  6. 会不会导致内存泄漏_可能会导致.NET内存泄露的8种行为

    原文连接:https://michaelscodingspot.com/ways-to-cause-memory-leaks-in-dotnet/ 作者 Michael Shpilt.授权翻译,转载请 ...

  7. Lenovo/Iomega的NAS存在权限控制缺陷,导致海量信息泄露

    最近,Lenovo发布了Lenovo/Iomega旗下NAS设备的安全通告.这些设备被安全研究人员发现存在信息泄露漏洞,导致大量敏感数据暴露在公网. 研究人员警告到,LenovoEMC(Lenovo和 ...

  8. android Handler避免内存泄露handler.removeCallbacksAndMessages(null)的使用

    今天,简单讲讲android如何使用 handler.removeCallbacksAndMessages(null). 这个其实很简单,之前我也写了一篇博客将关于handler.removeMess ...

  9. 【Java报错】记录一次 sun.misc.Unsafe.park(Native Method) Conflicting setter definitions for property 导致的内存泄露

    1. 报错信息 2021-10-29 08:44:56 WARN [,,,] [main] o.a.c.loader.WebappClassLoaderBase - The web applicati ...

最新文章

  1. 教你如何在 AlertManager 报警通知中展示监控图表
  2. 初识sparklyr—电影数据分析
  3. 银行事后监督及票据影像光盘缩微系统
  4. ubuntu下vi/vim 的基本使用法
  5. 直播预告:基于强化学习的关系抽取和文本分类 | PhD Talk #18
  6. Visual Studio 快速返回上次浏览/编辑的位置
  7. 英文连词_连词我们…讨厌
  8. 使用pt-query-digest时遇到报错can't locate Digest/MD5.pm in @INC (@INC contains: /usr/local/lib64/perl5
  9. The J2EE Architect's Handbook讀書筆記(一)
  10. kong组件_使用Kongzue全家桶组件创建项目全攻略(一、引入和沉浸式的那些事儿)...
  11. 报名国电没有计算机二级,想进入国电,捧起“铁饭碗”?没问题,这几个专业助你成功...
  12. 一个html可以有几个h1,一个页面可以有多个H1标签吗?
  13. OPIE(火狐扩展)-导入导出firefox扩展的所有设置
  14. 4.2V锂电池充电、放电保护电路分享
  15. 汽车软件常用名称缩写
  16. Cortex-M3 (NXP LPC1788)之EEPROM存储器
  17. 基于Python的随机森林(RF)回归与变量重要性影响程度分析
  18. Mybatis使用choose when
  19. 中华第一考----系统架构设计师考试
  20. Mac上的PowerPoint保存时出现“连同字体保存”——“某些字体无法随演示文稿一起保存。仍然要保存演示文稿吗?”

热门文章

  1. 提供呼叫中心服务器,呼叫中心系统方案
  2. android+tv+无线键盘,手机键盘有那些 5款适合手机使用的无线键盘推荐
  3. idea取消comiit_IDEA 合并多次commit为一个?
  4. php任意文件删除漏洞,phpshe后台任意文件删除漏洞及getshell | CN-SEC 中文网
  5. axios获取图片显示_vue中使用axios post上传头像/图片并实时显示到页面的方法
  6. java 导出excel二维表,如何轻松将EXCEL二维统计表转为数据清单?
  7. mysql索引分析和优化_MySQL索引分析和优化
  8. python123文件和数据格式化测试7_二级Python----文件和数据格式化(DAY 7)
  9. java map null吗_Java: Map里面的键和值可以为空吗?
  10. 一个给计算机写程序的人是谁,如果计算机是中国人发明的 写代码会是一种什么情况 程序员看了都头疼...