展会信息港展会大全

android开发Context泄漏:Handlers & Inner Classes
来源:互联网   发布日期:2016-01-19 12:29:49   浏览:1640次  

导读:先思考下面一个代码片段123456789publicclass SampleActivity extends Activity { privatefinal Handler mLeakyHandler = new Handler() { @Override publicvoid handleMessage(Mes ...

先思考下面一个代码片段

1

2

3

4

5

6

7

8

9

publicclass SampleActivity extends Activity {

privatefinal Handler mLeakyHandler = new Handler() {

@Override

publicvoid handleMessage(Message msg) {

// ...

}

}

}

虽然不容易发现,但上面的代码会导致大量的内存泄漏。如果是在Esclipse中编写代码的话 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当中,非静态的和匿名的内部类会隐式的持有它们外部类的一个引用

那么到底在哪里内存泄漏了呢?虽然非常的隐秘微妙,但是咋们不妨来看看下面一段代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

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定义成一个静态的属性(静态的匿名类对象不会持有外部类的引用),如下代码所示

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

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 android

AiLab云推荐
推荐内容
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港