0%

Android之Handler内存泄漏及解决方式

Android开发之内存泄漏

此文参考自Android内存泄露
在Android开发中,内存泄漏非常常见。

所谓内存泄漏,就是本来应该被回收的对象未能被回因而导致对象留在内存中。
内存泄漏产生的原因:当一个对象不再被使用时,本该回收但是此时有另外一个正在使用的对象持有它的引用从而导致它不能被回收
在Android开发中,Handler内存泄漏极为常见。Handler的用法有:新建Handler子类匿名内部类
匿名内部类使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//匿名内部类
Handler myHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//处理业务逻辑
}
};
//启动子线程
new Thread(new Runnable() {
@Override
public void run() {
//定义发送的消息
Message message = Message.obtain();
message.what = UPDATE;
//向MessageQueue队列发送消息,传入主线程的Handler
myHandler.sendMessage(message);
}
}).start();

定义内部类方式

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
//静态Handler类配合弱引用使用
class MyHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case UPDATE:
text.setText("nice");
break;
default:
break;
}
}
}
}
MyHandler myHandler = new MyHandler();

//启动子线程
new Thread(new Runnable() {
@Override
public void run() {
//定义发送的消息
Message message = Message.obtain();
message.what = UPDATE;
//向MessageQueue队列发送消息,传入主线程的Handler
myHandler.sendMessage(message);
}
}).start();

这两种方式并没有太大的区别。
不过上述两种方式都有内存泄漏的风险。

如上图所示,Android Studio提示Handler没有被设置为静态类的时候会造成泄漏。

Handler泄漏原因

在java中,静态内部类匿名内部类都默认持有外部类的引用。Android中Handler消息队列还有未处理的消息时(或还有正在处理的消息),消息队列的Message会持有Handler实例的引用。而同时由于Handler的使用方式(非静态内部类和静态内部类),又会持有外部类的引用(MainActivity实例),这个引用链会一直保持,直到handler消息队列中的消息都被处理完。

在Handler队列还有未处理完的消息时或正在处理消息时,此时若需要销毁外部类,但是由于引用关系,GC(垃圾回收器)无法回收MainActivity,从而造成内存泄漏。

解决方法一(静态内部类+弱引用)

解决这个可以用静态Handler内部类加上弱引用的方式
因为静态内部类不会持有外部类的引用,从而使得”未被处理/正在处理的消息–>Handler实例–>外部类”这条引用链不成立。
同时还可以加上WeakReference弱引用持有Activity实例,因为弱引用对象拥有短暂的生命周期,在GC的时候一旦发现了只具有弱引用的对象,不管内存空间是否足够,都会回收它。

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
//静态Handler类配合弱引用使用
static class MyHandler extends Handler {
//定义弱引用实例
private WeakReference<MainActivity> mainActivityWeakReference;
//在构造方法中传入需要持有的Activity实例
private MyHandler (MainActivity activity) {
mainActivityWeakReference = new WeakReference<>(activity);
}

@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
MainActivity activity = mainActivityWeakReference.get();
if (activity != null) {
switch (msg.what) {
case UPDATE:
text.setText("nice");
break;
default:
break;
}
}
}
}

private MyHandler myHandler = new MyHandler(this);
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bu:
new Thread(new Runnable() {
@Override
public void run() {
<!-- Message message = Message.obtain();
message.what = UPDATE;
//发送消息
myHandler.sendMessage(message); -->
//code
}
}).start();
}
}

解决方法二(外部类结束生命周期时,清空Handler内的消息队列)

1
2
3
4
5
@Override
protected void onDestroy() {
super.onDestroy();
myHandler.removeCallbacksAndMessages(null);
}

建议:为了保证Handler中消息队列所有消息都能被执行,建议使用静态Handler内部类 + 弱引用的方式。