本人翻译, 略有改动, 原文地址如下:
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------------------------
相关推荐
官方离线安装包,亲测可用。使用rpm -ivh [rpm完整包名] 进行安装
官方离线安装包,亲测可用
前端开源库-connect-assetmanager-handlers-updated.zip
Laravel开发-laravel-exception-handlers Laravel 5的异常处理程序
前端开源库-connect-assetmanager-handlers-updated连接更新的AssetManager处理程序,为Connect AssetManager发布和预挂接。
开源项目-gorilla-handlers.zip,gorilla/handlers: A collection of useful handlers for Go's net/http package
资源来自pypi官网。 资源全名:fabric-am-handlers-0.8.tar.gz
资源来自pypi官网。 资源全名:django-channels-handlers-0.2.0.tar.gz
资源分类: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 = ...
官方离线安装包,亲测可用。使用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 ...
官方版本,亲测可用
官方版本,亲测可用
官方版本,亲测可用
官方版本,亲测可用
官方版本,亲测可用
官方版本,亲测可用