在Android开发中,Handler是一个非常重要的组件,它可以用来实现线程之间的通信和任务调度。本篇文章将介绍Handler的使用方式和原理,帮助读者更好地理解Android开发中的线程处理。
什么是Handler?
Handler是Android中的一个消息处理器,它可以接收并处理其他线程发来的消息。简单来说,Handler就是一个用来处理消息的工具类,它可以将消息发送给其他线程,也可以接收其他线程发送的消息进行处理。
Handler的使用方式
使用Handler的基本流程为:创建Handler对象 -> 发送消息 -> 处理消息。
在使用 Handler 之前,需要了解一些相关概念:
- 线程:是独立运行的程序段,执行的代码是一个单独的任务。
- 消息队列:是一种存储消息的数据结构,支持先进先出的队列操作。
- Looper:可以让线程不停地从消息队列中取出消息并处理,是线程与消息队列交互的桥梁。
- Message:是 Android 中处理消息的基本类,可以携带一些数据,用于在 Handler 中进行处理。
创建Handler对象
在使用Handler之前,需要先创建一个Handler对象。创建Handler对象的方式有两种:
在主线程中创建Handler对象:
在主线程中创建Handler对象非常简单,只需要在主线程中创建一个Handler对象即可:
代码语言:javascript复制Handler handler = new Handler();
在子线程中创建Handler对象:
在子线程中创建Handler对象需要先获取到主线程的Looper对象,然后使用Looper对象来创建Handler对象:
代码语言:javascript复制Handler handler = new Handler(Looper.getMainLooper());
发送消息
创建Handler对象之后,就可以使用它来发送消息了。发送消息的方式有两种:
使用Handler的post()方法:
使用Handler的post()方法可以将一个Runnable对象发送到Handler所在的消息队列中。Runnable对象中的代码会在Handler所在的线程中执行。
代码语言:javascript复制handler.post(new Runnable() {
@Override
public void run() {
// 在Handler所在的线程中执行的代码
}
});
使用Handler的sendMessage()方法:
使用Handler的sendMessage()方法可以将一个Message对象发送到Handler所在的消息队列中。Message对象中可以携带一些数据,用于在Handler中进行处理。
代码语言:javascript复制Message message = new Message();
message.what = 1;
message.obj = "Hello World!";
handler.sendMessage(message);
除了基本用法,Handler还有一些高级用法,下面列举了几个常用的:
- 使用HandlerThread创建带有消息队列的线程,避免频繁地创建线程;
- 使用Message.obtain()来获取Message对象,避免频繁地创建对象;
- 使用Handler的sendEmptyMessage()方法来发送空消息。
处理消息
当其他线程发送消息到Handler所在的消息队列中时,Handler就会接收到这些消息并进行处理。处理消息的方式有两种:
重写Handler的handleMessage()方法:
重写Handler的handleMessage()方法可以处理其他线程发送的消息。handleMessage()方法中的代码会在Handler所在的线程中执行。
代码语言:javascript复制Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
String message = (String) msg.obj;
// 处理消息的代码
break;
default:
break;
}
}
};
实现Handler.Callback接口:
实现Handler.Callback接口可以处理其他线程发送的消息。Callback接口中的方法会在Handler所在的线程中执行。
代码语言:javascript复制Handler.Callback callback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 1:
String message = (String) msg.obj;
// 处理消息的代码
break;
default:
break;
}
return true;
}
};
Handler handler = new Handler(callback);
Handler的原理
在Handler的背后,实际上是使用了消息队列和线程通信的机制。当其他线程发送消息时,消息会被加入到Handler所在的消息队列中。然后,Handler会从消息队列中取出消息进行处理。
消息队列和Looper
消息队列和Looper是 Handler 实现的基础。每个线程都有一个消息队列(Message Queue),消息队列中存储着队列的所有消息(Message)。线程通过一个 Looper 来管理它的消息队列,通过不断地从消息队列中读取消息,实现了线程的消息循环 (Message Loop) 的功能。
代码语言:javascript复制public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
// 一旦有消息,就会返回Message对象
msg.target.dispatchMessage(msg);
}
}
如上所示,一个线程中的消息无限循环直到队列里没有消息为止(MessageQueue.next()
)。消息通过 Message.target
属性来找到它想要执行的 Handler,从而被分配到正确的线程中并且得到执行。一旦有消息,就会调用 dispatchMessage(Message)
方法进行分发。
消息分发
消息分发是Handler 的核心部分,在它的内部逻辑中,也是最为关键的部分。
在 Handler 中,消息分发的流程如下:
1.1. 发送消息
由其他线程调用 Handler 的方法向消息队列中发送消息。
代码语言:javascript复制Handler handler = new Handler() ;
handler.post(new Runnable(){
@Override
public void run() {
// 在其他线程发送消息
}
});
1.2. 创建 Message 对象
将需要传输的数据封装成 Message 类型的对象,然后将该对象塞入消息队列中。
代码语言:javascript复制Message msg = new Message();
msg.obj = "消息内容";
handler.sendMessage(msg);
1.3. 将消息加入消息队列
Handler 将消息放入消息队列中。
在 Handler 内部,新构建的消息通过 enqueueMessage()
方法被加入到 MessageQueue
相应的内存块中,并且会在该内存块的标记 next
表示下一个内存块的索引号。
public void sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return;
}
msg.target = this;
queue.enqueueMessage(msg, uptimeMillis);
}
而 enqueueMessage()
方法的核心逻辑,就是紧接着找到消息队列中最近的一个时间戳比当前时间小的消息,将新消息插入到这个消息之后。
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg " This message is already in use.");
}
boolean needWake;
if (mBlocked) {
// If the queue is blocked, then we don't need to wake
// any waiters since there can be no waiters.
msg.markInUse();
needWake = false;
} else {
msg.markInUse();
needWake = mMessagesForQueue.enqueueMessage(msg, when);
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
1.4. Looper 开启消息循环
Looper 不断轮询内部 MessageQueue
中的消息,获取消息后在 Handler 中进行分发处理。
在 Looper 类中,强制让当前线程创建一个 Looper 对象,并通过调用 QualityLooper 构造函数 create 方法捕获该对象(一般用于构建线程的消息循环)。接下来,通过调用 run 方法被延迟1秒钟来启动上下文中的消息循环。
代码语言:javascript复制public static void prepare() {
prepare(true);
}
public static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
continue;
}
msg.target.dispatchMessage(msg);
}
}
这个方法是一个无限循环方法,在每个循环中,Looper 都会从自己的消息队列中获取一个消息,如果队列为空,则一直循环等待新的消息到来,直到被调用 quit()
方法,才终止循环。在获取到消息之后,调用 msg.target.dispatchMessage(msg)
进行消息的分发处理。
1.5. 查找目标 Handler
Looper 不断轮询消息队列,获取消息后,注意到 MessageQueue.next()
方法中有这样一行代码:
msg.target.dispatchMessage(msg);
1.6. 传递 Message 对象
从消息中获取到 target 属性,它就是当前这个Message对象所属的 Handler,并执行该Handler的 handleMessage(Message) 方法。
dispatchMessage(Message)
的核心代码是判断 Message.target
是否为 null,不为 null 则将消息传递给目标 Handler,如果为 null,则直接抛出异常。
void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 消息带有回调方法,如果 callback 不为空,那么就直接执行
handleCallback(msg);
} else {
if (mCallback != null) {
// 尝试将消息抛给 mCallback
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); // 如果消息中没有 callback,那就执行 handleMessage(msg)
}
}
// 处理具体的 Message
public void handleMessage(Message msg) {
switch (msg.what) {
// 根据消息类型分发处理
default:
break;
}
}
当 Handler 接收到消息时,它会回调自己的 handleMessage(Message)
方法处理消息。
在 handleMessage(Message)
方法中,我们可以编写各种不同的逻辑,并对当前情况下的消息进行处理。这通常包括对消息类型的检查以及消息携带的数据的解析和操作。
当我们在 handleMessage(Message)
方法中完成了所有处理后,我们就可以将数据发送回发送消息的线程,或将数据传递给其他线程进行进一步处理。
总结
本篇文章深入探讨了 Handler 的原理,主要包括了消息队列和 Looper 的相关概念,以及消息的发送和处理。除此之外,还讲了当不同线程的消息需要在 Handler 中处理时,需要用到 Looper、MessageQueue 和 Handler 这三个关键组件的协同工作。