本人翻译, 略有改动, 原文地址如下:
http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
考虑如下代码:
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { /* ... */ } } }
你可能看不出来这段代码会造成内存泄漏, 确实, 它不那么容易被发现. 如果你运行Android的lint工具, 它会给你一个警告, 提示你把handler定义成静态的(static), 否则可能造成内存泄漏. 但内存泄漏是怎么发生的呢?
首先, 我们应该知道如下几点:
1: 当一个android程序启动时, 框架层(framework)会为程序的主线程创建一个Looper对象. 该对象实现了一个简单的消息队列, 循环不断的处理队列上的消息对象(Message), 主线程上的Looper对象在程序的整个生命周期中一直存在.
2: 当一个Handler在主线程上被实例化时, 它就与Looper的消息队列关联到一起了. 队列上的Message对象持有一个handler的引用, 这使得Looper处理到某一个Message时, 能够调用handler.handleMessage()方法.
3: 在java里, 非静态内部类和匿名内部类持有一个隐式的外部类引用. 相反, 静态的内部类就没有该隐式引用.
那么, 泄露在哪里发生呢? 这个有点微妙, 考虑下面一个例子:
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() { public void run() { } }, 600000); // Go back to the previous Activity. finish(); } }
当Activityfinish后, 我们发出的那个"延迟处理消息"将在主线程的消息队列中保持10分钟, 直到该消息最终被处理. 由于消息持有一个handler的引用, 而handler又持有一个它的外部类-SampleActivity的引用, 这样就阻止了activity的context被垃圾回收, 从而泄漏了Activty引用的所有的应用资源. 注意上述例子中的匿名的Runnable对象也一样造成了context的泄露.
要避免这个问题, 就需要将Handler改为静态内部类. 如果你需要在Handler中调用Activity外部类的方法, 你可以在handler中使用一个WeakReference来持有activity对象.
(注意我们将Handler和Runnable都定义成了static的)
public class SampleActivity 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); // Instances of anonymous classes do not hold an implicit // reference to their outer class when they are "static". private final static Runnable sRunnable = new Runnable() { 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, 600000); // Go back to the previous Activity. finish(); } }
结论: 在Activity中使用非静态的内部类时, 尽量避免内部类生命周期超出了Activity之外. 类似的例子还有AsyncTask.
=================================
补充例子:
如果你看过PendingIntent的源代码, 你会看到它有一些send(Handler...)的方法, 如果某个Activity调用了PendingIntent.send(...), 并且传入一个非静态的内部Handler类, 当activity被销毁后, 内部类仍然持有它的引用, 导致它无法被垃圾收集.
当然, 如果你没有像这样不当的发布一个Handler到其它的类, 你就不用担心泄露发生.
=================================
如果你不想每次都创建一个WeakReference, 可以先创建这样一个通用类:
public abstract class WeakReferenceHandler<T> extends Handler { private WeakReference<T> mReference; public WeakReferenceHandler(T reference) { mReference = new WeakReference<T>(reference); } @Override public void handleMessage(Message msg) { if (mReference.get() == null) return; handleMessage(mReference.get(), msg); } protected abstract void handleMessage(T reference, Message msg); }
--------------------END------------------------
相关推荐
然后,在Django项目的settings.py文件中配置 Channels 和 `django-channels-handlers`,并根据库的文档指导创建相应的消费者类。 **文件结构** 在解压的`django-channels-handlers-0.2.0`目录下,通常会包含以下...
官方离线安装包,亲测可用。使用rpm -ivh [rpm完整包名] 进行安装
官方离线安装包,亲测可用
在前端开发领域,开源库是开发者们常用的工具,它们提供了丰富的功能,帮助我们高效地构建和管理项目。本文将深入探讨“connect-assetmanager-handlers-updated”这一前端开源库的更新,以及它如何增强Connect ...
Fabric AM Handlers 0.8 是一个基于Python的库,它主要设计用于自动化管理和处理Amazon Web Services(AWS)的资源。这个库的版本0.8可以从PyPI(Python Package Index)官方网站下载,PyPI是Python开发者查找和安装...
`laravel-exception-handlers` 主题深入探讨了在Laravel 5中如何定制和管理异常处理。 1. **异常处理基础** - 在PHP中,异常是通过`throw`语句引发,并通过`try-catch`块来捕获和处理。Laravel扩展了这个概念,...
开源项目-gorilla-handlers.zip,gorilla/handlers: A collection of useful handlers for Go's net/http package
标题中的“connect-assetmanager-handlers-updated”指的是一个针对前端开发的开源库,它与Node.js中的Connect框架紧密相关。Connect是Express.js的基础,是一个轻量级的HTTP服务器中间件,广泛用于构建Web应用程序...
资源分类:Python库 所属语言:Python 资源全名:django-ohm2-handlers-light-0.1.19.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
资源分类:Python库 所属语言:Python 资源全名:good-handlers-1.0.0.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
handlers 纱线: yarn add ngrx-handlers :high_voltage: 用法NgRx动作和减速器// books-page.actions.tsexport const enter = createAction ( '[Books Page] Enter' ) ;export const updateSearchTerm = ...
这样不仅方便了数据的保存和分发,还能确保数据的安全性,避免在网络传输过程中可能出现的数据泄露风险。 压缩包中的文件包括了以下几种编程语言的导出处理器: 1. **地址.txt**:可能包含服务器路径或者导出服务...
官方离线安装包,亲测可用。使用rpm -ivh [rpm完整包名] 进行安装
This application demonstrates how to use the Format and Parse event handlers when data binding Windows Form controls. The Format event fires when the data is transferred the data source to the bound ...
Laravel-Elasticsearch-Handlers 在 Laravel 应用程序中使用官方 Elastic Search 客户端的更简单方法。 安装和配置 通过 composer 安装cviebrock/laravel-elasticsearch-handlers包: $ composer require ...
官方版本,亲测可用
官方版本,亲测可用
官方版本,亲测可用
官方版本,亲测可用
官方版本,亲测可用