在Android开发中,Handler是一个非常重要的组件,它可以用来完成线程之间的通信和任务调度。本篇文章将介绍Handler的运用办法和原理,帮助读者更好地了解Android开发中的线程处理。
什么是Handler?
Handler是Android中的一个音讯处理器,它可以接纳并处理其他线程发来的音讯。简略来说,Handler便是一个用来处理音讯的东西类,它可以将音讯发送给其他线程,也可以接纳其他线程发送的音讯进行处理。
Handler的运用办法
运用Handler的根本流程为:创立Handler方针 -> 发送音讯 -> 处理音讯。
在运用 Handler 之前,需求了解一些相关概念:
- 线程:是独立运转的程序段,履行的代码是一个单独的任务。
- 音讯行列:是一种存储音讯的数据结构,支撑先进先出的行列操作。
- Looper:可以让线程不停地从音讯行列中取出音讯并处理,是线程与音讯行列交互的桥梁。
- Message:是 Android 中处理音讯的根本类,可以带着一些数据,用于在 Handler 中进行处理。
创立Handler方针
在运用Handler之前,需求先创立一个Handler方针。创立Handler方针的办法有两种:
-
在主线程中创立Handler方针:
在主线程中创立Handler方针非常简略,只需求在主线程中创立一个Handler方针即可:
Handler handler = new Handler();
-
在子线程中创立Handler方针:
在子线程中创立Handler方针需求先获取到主线程的Looper方针,然后运用Looper方针来创立Handler方针:
Handler handler = new Handler(Looper.getMainLooper());
发送音讯
创立Handler方针之后,就可以运用它来发送音讯了。发送音讯的办法有两种:
-
运用Handler的post()办法:
运用Handler的post()办法可以将一个Runnable方针发送到Handler地点的音讯行列中。Runnable方针中的代码会在Handler地点的线程中履行。
handler.post(new Runnable() { @Override public void run() { // 在Handler地点的线程中履行的代码 } });
-
运用Handler的sendMessage()办法:
运用Handler的sendMessage()办法可以将一个Message方针发送到Handler地点的音讯行列中。Message方针中可以带着一些数据,用于在Handler中进行处理。
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地点的线程中履行。
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地点的线程中履行。
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) 的功用。
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 的办法向音讯行列中发送音讯。
Handler handler = new Handler() ;
handler.post(new Runnable(){
@Override
public void run() {
// 在其他线程发送音讯
}
});
1.2. 创立 Message 方针
将需求传输的数据封装成 Message 类型的方针,然后将该方针塞入音讯行列中。
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秒钟来发动上下文中的音讯循环。
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 这三个关键组件的协同作业。
推荐
android_startup: 提供一种在运用发动时可以愈加简略、高效的办法来初始化组件,优化发动速度。不仅支撑Jetpack App Startup的悉数功用,还提供额外的同步与异步等待、线程控制与多进程支撑等功用。
AwesomeGithub: 根据Github的客户端,纯操练项目,支撑组件化开发,支撑账户密码与认证登陆。运用Kotlin语言进行开发,项目架构是根据JetPack&DataBinding的MVVM;项目中运用了Arouter、Retrofit、Coroutine、Glide、Dagger与Hilt等流行开源技能。
flutter_github: 根据Flutter的跨渠道版别Github客户端,与AwesomeGithub相对应。
android-api-analysis: 结合具体的Demo来全面解析Android相关的知识点, 帮助读者可以更快的掌握与了解所论述的关键。
daily_algorithm: 每日一算法,由浅入深,欢迎参加一起共勉。