1. 简介
Handler是一套 Android 消息传递机制,主要用于线程间通信。
handler其实就是主线程在起了一个子线程,子线程运行并生成Message,Looper获取message并传递给Handler,Handler逐个获取子线程中的Message.
Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信
在实际开发中,为了UI操作的线程安全,规定只允许UI线程更新Activity里的组件。因此,使用Handler消息传递机制,即工作线程需要更新UI时,通过Handler通知UI主线程,从而在主线程更新UI操作。
2. 相关概念解释
概念 | 定义 | 作用 | 备注 |
---|---|---|---|
主线程 (UI线程、Main Thread) | app第一次启动时,会自动开启一条主线程 | 处理UI事件 (如更新、操作等) | |
子线程 (工作线程) | 人为手动开启的线程 | 执行耗时操作 (如网络请求、数据加载等) | |
消息 (Message) | 线程间通讯的数据单元 (即Handler接受&处理的消息对象) | 存储需操作的通信信息 | / |
消息队列 (Message Queue) | 存储Handler发送过来的消息(Message) | / | |
处理者 (Handler) | 主线程和子线程的通信媒介 线程消息的主要处理者 | 添加消息(Message)到消息队列(Message Queue) 处理循环器(Looper)分派过来的消息(Message) | / |
循环器 (Looper) | 消息队列(Message Queue)和处理者(Handler)的通信媒介 | 消息循环,即 消息获取:循环取出消息队列(Message Queue)的消息(Message) 消息分发:将取出的消息(Message)发送给对应的处理者(Handler) | 每个线程中只能拥有一个Looper 一个Looper可绑定多个线程的Handler 即:多个线程可往1个Looper所持有的Message Queue中发送消息,提供了线程间通信的可能 |
2.1 Android消息机制
2.2 Handler示意图
2.3 消息机制的模型
- Message:需要传递的消息,可以传递数据;
- MessageQueue:消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
- Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
- Looper:不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。
3. Handler的基本使用
3.1 创建Handler
由于Java特性,内部类会持有外部类,Activity会被Handler持有,如果Handler允许发送延时消息、而如果在延时期间用户关闭了Activity,那么Activity会泄露。
**解决方案:**将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息。
public class HandlerActivity extends AppCompatActivity {
private Button bt_handler_send;
private static class MyHandler extends Handler {
//弱引用持有HandlerActivity , GC 回收时会被回收掉
private WeakReference<HandlerActivity> weakReference;
public MyHandler(HandlerActivity activity) {
this.weakReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity = weakReference.get();
super.handleMessage(msg);
if (null != activity) {
//执行业务逻辑
Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.HandlerActivity);
//创建 Handler
final MyHandler handler = new MyHandler(this);
bt_handler_send = findViewById(R.id.bt_handler_send);
bt_handler_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
//使用 handler 发送空消息
handler.sendEmptyMessage(0);
}
}).start();
}
});
}
@Override
protected void onDestroy() {
//移除所有回调及消息
myHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}
3.2 Message获取
获取 Message 大概有如下几种方式:
Message message = myHandler.obtainMessage(); //通过 Handler 实例获取
Message message1 = Message.obtain(); //通过 Message 获取
Message message2 = new Message(); //直接创建新的 Message 实例
// 为了节省开销,我们在使用的时候尽量复用 Message,使用前两种方式进行创建。
// 通过查看源码可知,Handler 的 obtainMessage() 方法也是调用了 Message 的 obtain() 方法
public final Message obtainMessage()
{
return Message.obtain(this);
}
3.3 Handler发送消息
Handler 提供了一些列的方法让我们来发送消息,如 send()
系列 、post()
系列(需要传入一个Runnalbe
对象)
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
不过不管我们调用什么方法,最终都会走到 MessageQueue.enqueueMessage(Message,long)
方法。
以 sendEmptyMessage(int)
方法为例:
//Handler
sendEmptyMessage(int)
-> sendEmptyMessageDelayed(int,int)
-> sendMessageAtTime(Message,long)
-> enqueueMessage(MessageQueue,Message,long)
-> queue.enqueueMessage(Message, long);
4. 两个实例(主线程-子线程)
4.1 子线程向主线程
首先我们在MainActivity中添加一个静态内部类,并重写其handleMessage方法。
private static class MyHandler extends Handler {
// mTarget持有对MainActivity的弱引用
// 允许你持有一个对象的引用,但不会阻止该对象对被GC回收
private final WeakReference<MainActivity> mTarget;
public MyHandler(MainActivity activity) {
mTarget = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
HandlerActivity activity = mTarget.get();
// 如果从弱引用中得到的activity不为null(意味着它还没有被垃圾收集),那么会进行一些业务逻辑的处理。
if (null != activity) {
//执行业务逻辑
if (msg.what == 0) {
Log.e("myhandler", "change textview");
MainActivity ma = mTarget.get();
ma.textView.setText("hahah");
}
Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
}
}
}
然后创建一个类型为MyHandler的私有属性:
private Handler handler1 = new MyHandler(this);
最后在onCreate回调中创建一个线程,用于接收发送消息:
new Thread(new Runnable() {
@Override
public void run() {
handler1.sendEmptyMessage(0);
}
}).start();
总结如下:
new Thread(new Runnable() {
@Override
public void run() {
handler1.sendEmptyMessage(0);
}
}).start();
4.2 主线程向子线程
首先创建一个MyHandler类。
private static class MyHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
Log.e("child thread", "receive msg from main thread");
}
}
}
声明一个Handler类型的私有变量,进行默认初始化为null。
private Handler handler1;
创建子线程,使handler指向新创建的MyHandler对象。
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler1 = new MyHandler();
Looper.loop();
Log.e("child thread", "child thread end");
}
}).start();
在主线程中向子线程中发送消息
while (handler1 == null) {
}
handler1.sendEmptyMessage(0);
handler1.getLooper().quitSafely();
5. Handler机制原理
5.1 Handler原理
普通的线程是没有looper的,如果需要looper对象,那么必须要先调用Looper.prepare
方法,并且一个线程只能有一个looper
Handler是如何完成跨线程通信的?
Android采用管道通信,所谓“管道”,其实就是一个文件,管道的两端是对应同一个文件的文件描述符(分别用于读写)。
- Handler通过sendMessage()发送Message到MessageQueue队列
- Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理
- 经过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理
- 将Message加入MessageQueue时,处往管道写入字符,可以会唤醒loop线程;如果MessageQueue中- 没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,往往用于做一些清理性地工作
为什么采用管道而非Binder?
- 从内存角度:通信过程中Binder还涉及一次内存拷贝,handler机制中的Message根本不需要拷贝,本身就是在同一个内存。Handler需要的仅仅是告诉另一个线程数据有了。
- 从CPU角度:为了Binder通信底层驱动还需要为何一个binder线程池,每次通信涉及binder线程的创建和内存分配等比较浪费CPU资源。