android中handler内存泄漏解决方法,Android 中 Handler 导致的内存泄漏

问题介绍

在Android开发过程中,我们经常用Handler来处理异步操作的返回结果,却没注意到很容易发生内存泄漏,代码通常如下:

1

2

3

4

5

6

7

8public class MainActivity extends Activity{

private Handler myHandler = new Handler() {

@Override

public void handleMessage(Message msg){

// do something

}

};

}

上面代码很常见,但是却是能引起内存泄露的,我们用Android Lint工具检查一下代码,会发现如下警告:

1

2

3Handler reference leaks

MainActivity.java

This Handler class should be static or leaks might occur

然后具体解释如下:

1

2

3

4

5

6

7

8

9Since this Handler is declared as an inner class, it may prevent the outer class from

being garbage collected.

If the Handler is using a Looper or MessageQueue for a thread other than the main thread,

then there is no issue.

If the Handler is using the Looper or MessageQueue of the main thread,

you need to fix your Handler declaration, as follows:

Declare the Handler as a static class; In the outer class, instantiate a WeakReference

to the outer class and pass this object to your Handler when you instantiate the Handler;

Make all references to members of the outer class using the WeakReference object.

问题分析

1. Android 方面

当Android应用启动的时候,系统会自动创建一个供主线程使用的Looper实例。

1

2

3

4

5

6

7

8/// file: frameworks/base/core/java/android/app/ActivityThread.java

public static void main(String[] args){

...

// 主线程中调用Looper.prepareMainLooper()方法创建Looper

Looper.prepareMainLooper();

...

}

Looper的主要工作就是一个一个处理消息队列(MessageQueue, Looper构造方法中创建)中的消息对象。在Android中,所有Android框架的事件(比如Activity的生命周期方法调用和按钮点击等)都是放入到消息中,然后加入到Looper要处理的消息队列中,由Looper负责一条一条地进行处理。

我们在主线程中实例化一个Handler时,如果没有指定其它的Looper,那么它就会自动使用主线程的Looper,如下图所示log:

9c3edbd36b89cb7468037286557004e6.png

所以我们发送一条消息到此Handler时,实际上消息是进入了主线程的消息处理队列,而此消息已经包含了一个Handler实例的引用:

1

2

3

4

5

6

7

8

9

10/// file: frameworks/base/core/java/android/os/Handler.java

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){

// 消息获得当前Handler的引用

msg.target = this;

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

}

当Looper来处理消息时,会据此引用来回调[Handler#handleMessage(Message)]:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22public static void loop(){

final Looper me = myLooper();

final MessageQueue queue = me.mQueue;

...

for (;;) {

Message msg = queue.next(); // might block

if (msg == null) {

// No message indicates that the message queue is quitting.

return;

}

...

try {

// 取到消息之后,交给发送该消息的Handler来处理消息

msg.target.dispatchMessage(msg);

} finally {

...

}

...

msg.recycleUnchecked();

}

}

2. Java方面

Java中的非静态内部类以及匿名内部类会持有外部类的引用。静态内部类不会持有外部类的引用。

3. 内存泄漏所在

结合Android和Java方面的分析,我们应该很容易就知道了为什么会产生内存泄漏:

发消息到Handler,消息进入到主线程的消息队列后持有对Handler的引用;

做为非静态内部类,Handler又持有外部类(在这里是MainActivity)的引用;

只要消息没被处理,那么MainActivity对象就无法被垃圾回收器回收,进而导致MainActivity持有的很多资源都无法回收,这就是我们常说的内存泄漏。

泄漏解决方案避免使用非静态内部类,前面有提到过静态内部类不会持有外部类的引用;

将Handler放在单独的文件中,而不是以内部类方式存在;

当需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20public class MainActivity extends AppCompatActivity{

// 静态内部类,不会持有外部类的引用

private static class MyHandler extends Handler{

// 外部类的弱引用

private WeakReference mActivity;

public MyHandler(MainActivity activity){

mActivity = new WeakReference(activity);

}

@Override

public void handleMessage(Message msg){

MainActivity activity = mActivity.get();

// 确保外部类没有被回收

if (activity != null) {

// Do something ...

}

}

}

}

小结

其实在Android中很多的内存泄露都是由于在Activity中使用了非静态内部类导致的,就像本文提到的一样,所以当我们使用时要非静态内部类时要格外注意,如果其实例的持有对象的生命周期大于其外部类对象,那么就有可能导致内存泄露。个人倾向于使用文章的静态类和弱引用的方法解决这种问题。

课后思考

如下代码是否存在内存泄漏?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21public class MainActivity extends AppCompatActivity{

private static class MyHandler extends Handler{

@Override

public void handleMessage(Message msg){

}

}

private final MyHandler myHandler = new MyHandler();

@Override

protected void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

myHandler.postDelayed(new Runnable() {

@Override

public void run(){

}

}, 1000 * 60 * 10);

finish();

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值