一. 前言

1. 什么是Handler?

A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue。

简单地说,就是一套 Android 传递机制。

2. 如何使用 Handler?

① 创建Message - obtainMessage

② 发送Message - sendMessage,post

③ 处理Message - handleMessage

3. Message

① 两个整型值,arg1,arg2,轻量级存储int类型的数据

② 一个Object值,任意对象

③ replyTo,线程通信的时候使用

④ what,用户自定义的消息码让接受者识别消息

二. Handler 的 工作原理

Android Handler:图文解析 Handler通信机制 的工作原理

三. Handler的使用方式

Android:这是一份Handler消息传递机制 的使用教程

四. Handler源码分析

Android:这是一份Handler消息传递机制 的使用教程

五. Handler的内存泄漏

Android 内存泄露:详解 Handler 内存泄露的原因

六. 实际例子

1. 常用的方法

1
2
//发送空的消息
public final boolean sendEmptyMessage(int what)
1
2
//发送消息
public final boolean sendMessage(@NonNull Message msg)
1
2
//延迟发送消息
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis)
1
2
//执行某一些操作
public final boolean post(@NonNull Runnable r)
1
2
//延迟执行某一些操作
public final boolean postDelayed(@NonNull Runnable r, long delayMillis)

2. 实战例子

① 没有解决内存泄漏的版本

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
public class MainActivity extends AppCompatActivity {

private static final int MESSAGE_CODE = 1000;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

@SuppressLint("HandlerLeak") final
//处理消息
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);

//接受消息
switch (msg.what){
case MESSAGE_CODE:
Toast.makeText(MainActivity.this, (String) msg.obj, Toast.LENGTH_SHORT).show();
break;
}
}
};

//发送消息
findViewById(R.id.send).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//创建消息
Message message = handler.obtainMessage();
message.arg1 = 1;
message.arg2 = 2;
message.obj = "message";
message.what = MESSAGE_CODE;
//发送消息
handler.sendMessage(message);
}
});

}
}

② 解决内存泄漏的版本

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
45
46
47
48
49
50
51
52
53
54
55
56
57
public class MainActivity extends AppCompatActivity {

private static final int MESSAGE_CODE = 1000;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//处理消息
final Handler handler = new MyHandler(this);

//发送消息
findViewById(R.id.send).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//创建消息
Message message = handler.obtainMessage();
message.arg1 = 1;
message.arg2 = 2;
message.obj = "message";
message.what = MESSAGE_CODE;
//发送消息
handler.sendMessage(message);

}
});
}

/**
* 自定义的解决内存泄漏的Handler
*/
private static class MyHandler extends Handler{

//定义弱引用实例
private WeakReference<Activity> reference;

//构造方法中传入所需的Activity
public MyHandler(Activity activity) {
//使用WeakReference弱引用持有Activity实例
this.reference = new WeakReference<>(activity);
}

@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);

//接受消息
switch (msg.what){
case MESSAGE_CODE:
Activity main = reference.get();
Toast.makeText(main, (String) msg.obj, Toast.LENGTH_SHORT).show();
break;
}
}
}
}

1> 原因:

Handler消息队列 还有未处理的消息 / 正在处理消息时,消息队列中的Message持有Handler实例的引用

由于Handler = 非静态内部类 / 匿名内部类(2种使用方式),故又默认持有外部类的引用(即MainActivity实例)

上述的引用关系会一直保持,直到Handler消息队列中的所有消息被处理完毕

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

2> 解决方案:

从上面可看出,造成内存泄露的原因有2个关键条件:

  1. 存在“未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系
  2. Handler的生命周期 > 外部类的生命周期

Handler消息队列 还有未处理的消息 / 正在处理消息 而 外部类需销毁。

方式一:静态内部类+弱引用

静态内部类 不默认持有外部类的引用,从而使得 “未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系 的引用关系 不复存在。

使用WeakReference弱引用持有Activity实例,弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

方式二:当外部类结束生命周期时,清空Handler内消息队列

不仅使得 “未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系 不复存在,同时 使得 Handler的生命周期(即 消息存在的时期) 与 外部类的生命周期 同步。

1
2
3
4
5
6
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
// 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
}

为了保证Handler中消息队列中的所有消息都能被执行,此处推荐使用解决方案1解决内存泄露问题,即 静态内部类 + 弱引用的方式。

③ 源码

Handler