Context泄漏:Handlers & Inner Classes
来源:互联网 发布:淘宝运费险保费 编辑:程序博客网 时间:2024/06/13 09:13
先思考下面一个代码片段
publicclass SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override publicvoid handleMessage(Message msg) { // ... } }}
虽然不容易发现,但上面的代码会导致大量的内存泄漏。如果是在Eclipse中编写代码的话Android Lint会警告:In Android,Handlers should be static or leaks might occur。但是内存泄漏到底是在哪发生的和到底是怎么发生的?现在就让我们来确定问题的根源,首先我们知道的是:
1、当一个Android应用启动时,Android平台框架会为应用的UI主线程创建一个Looper对象。Looper简单的实现了一个消息队列,采用队列的方式处理一个个消息。所有主要的应用程序框架中的事件都包含在消息对象中,所有这些事件消息都加入到Looper的消息队列中,一个接着一个的处理掉。并且UI主线程的Looper对象贯穿于应用程序的整个生命周期。
2、当一个Handler对象在UI主线程中经过实例化后,就与Looper消息队列相关联了。当一个消息发布进消息队列中时,Looper对象会持有一个Handler对象,以便于当Loope处理此消息时,Android平台框架能够调用Handler对象的
Handler#handleMessage(Message)
方法来进行处理 3、在Java当中,非静态的和匿名的内部类会隐式的持有它们外部类的一个引用
那么到底在哪里内存泄漏了呢?虽然非常的隐秘微妙,但是咋们不妨来看看下面一段代码
publicclass SampleActivity extends Activity { privatefinal Handler mLeakyHandler = new Handler() { @Override publicvoid handleMessage(Message msg) { // ... } } @Override protectedvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mLeakyHandler.postDelayed(newRunnable() { @Override publicvoid run() { } },60 * 10 * 1000); // Go back to the previous Activity. finish(); }}
当这个Activity结束销毁时,推迟的处理的消息依然会存在于消息队列中10分钟直到被处理掉。消息对象持有Activity的Handler对象的引用,而Handler对象持有一个隐式的外部类的引用(在上面的例子中是SampleActivity)。这个Activity引用会一直存在直到该消息被处理掉,因此阻止了垃圾回收器对Activity Context对象的回收,泄漏了所有的应用程序资源。上面的代码片中的第15行,非静态Runnable匿名类隐式持有Activity一个引用,而非静态匿名类对象Handler又隐式的持有Activity引用,所以Activity Context对象泄露了。
要解决此问题,我们可以使用一个外部的Handler子类(Activity Context依然可能会被泄漏,稍后会提到规避的方法)或者使用一个静态的Handler子类内部类。静态内部类并不会隐式的持有外部类的一个引用,所以不会泄漏Activity Context对象。但是如果你希望在静态内部类Handler子类中使用外部类Activity中的一些属性或方法,我们可以在静态内部类中持有一个外部类Activity的弱引用,这样Activity Context对象就不会泄漏了。当然我们也要把Runnable定义成一个静态的属性(静态的匿名类对象不会持有外部类的引用),如下代码所示
publicclass SampleActivity extends Activity { /** * Instances of static inner classes do not hold an implicit * reference to their outer class. */ privatestatic class MyHandler extends Handler { privatefinal WeakReference<SampleActivity> mActivity; publicMyHandler(SampleActivity activity) { mActivity = new WeakReference<SampleActivity>(activity); } @Override publicvoid handleMessage(Message msg) { SampleActivity activity = mActivity.get(); if(activity != null) { // ... } } } privatefinal MyHandler mHandler = new MyHandler(this); /** * Instances of anonymous classes do not hold an implicit * reference to their outer class when they are "static". */ privatestatic final Runnable sRunnable = new Runnable() { @Override publicvoid run() { } }; @Override protectedvoid 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(); }}
虽然静态和非静态内部类之间的区别是相当微小的,但是每个Android开发者都应当知道。我们应当尽量避免在Activity中使用生命周期长于Activity生命周期的内部类,而应当使用静态内部类并且持有Activity的一个弱引用方式。
- Context泄漏:Handlers & Inner Classes
- How to Leak a Context: Handlers & Inner Classes
- How to Leak a Context: Handlers & Inner Classes
- Context是怎么泄露的:Handlers & Inner Classes
- How to Leak a Context: Handlers & Inner Classes
- Handle & Inner Classes 如何避免内存泄漏
- context是如何泄漏的 - Handlers和内部类
- context是如何泄漏的 - Handlers和内部类
- context是如何泄漏的 - Handlers和内部类
- context是如何泄漏的 - Handlers和内部类
- Inner Classes
- Handler、Inner Class 怎么造成context泄漏的?
- Nested Classes和Inner Classes
- Interfaces & Inner Classes
- Inner Classes Example
- Closures callbacks Inner Classes
- Java中的Nested Classes和Inner Classes
- 第八章: Interfaces & Inner Classes
- 复习快速排序
- NO.90 Struts2.3.15.1升级总结
- Essential C++中文版——满汉全席之外
- android:仪表盘(简单易实现)
- PL/SQL表---table()函数用法
- Context泄漏:Handlers & Inner Classes
- JSR286 Portlet开发:Portlet之间的Events通信
- 再谈DOS批处理下格式化日期字符串的方法(详解)
- java List 排序 Collections.sort() 对 List 排序
- 浅析大数据量高并发的数据库优化
- Ping 命令详解收集
- android--ndk交叉编译工具安装
- poj 1631(Bridging signals LIS)nlogn
- linux socket 网络编程一般步骤