异步消息处理机制主要由 Message、Handler、Looper 以及 MessageQueue 构成,它们共同协作实现线程间的通信与任务调度。
─────────────────────── 【基本概念】
-
Looper 与 MessageQueue
- 每个线程可以拥有一个 Looper。Looper 会不断循环,从与之相关联的 MessageQueue 中取出消息逐一分发和处理。
- 主线程默认已经有 Looper,即主 Looper;而其他线程需要通过 HandlerThread 或手动调用 Looper.prepare() 和 Looper.loop() 来创建。
Looper 是 Android 异步消息处理机制中的核心组件之一。它主要负责以下功能:
-
管理消息队列(MessageQueue):Looper 会不断循环,从消息队列中取出 Message 消息,并将这些消息分发给相应的 Handler 处理。
-
在特定线程中执行消息循环:每个线程至多拥有一个 Looper,该 Looper 控制着该线程的消息循环。Android 的主线程(UI 线程)在应用启动时就已经存在一个默认的 Looper,可以通过 Looper.getMainLooper() 获取。
-
维持线程等待并处理异步任务:通过与 Handler 配合使用,Looper 能让线程在没有任务时处于等待状态,一旦有消息到来就进行处理。这种机制可以让线程不断地处理异步任务,而不是不断轮询或空转。
Looper 是 Android 中实现异步消息处理的关键组件,它使得线程能够便捷地进行任务调度和消息处理,而无需自己管理复杂的线程间同步问题。
-
Handler
- Handler 负责将消息发送到指定线程的 MessageQueue 中,并定义如何处理这些消息。
- 当 Handler 与一个 Looper 绑定后,Handler 的 handleMessage() 方法就是在该线程中运行的。
Handler 是 Android 异步消息处理机制中的核心组件,它主要起到以下作用:
- 将消息或任务(Runnable)加入到与特定线程关联的消息队列中。
- 分发这些消息或任务到对应线程,使其在该线程的上下文中执行。
在 Android 中,每个 Handler 都关联一个 Looper 和它的消息队列。通过 Handler,你可以在任何线程中发送消息或任务,并由绑定的线程的 Looper 进行循环处理。通常,我们在主线程中使用 Handler 来安排 UI 更新,也可以在子线程中使用 Handler 执行后台任务。
-
Message
- Message 用于封装需要传递的数据,可以通过 what、obj、arg1、arg2 等属性携带信息。
- Handler 通过 sendMessage()、post() 等方法将消息传递到 MessageQueue,同线程中的 Looper 去轮询消息并调用对应 Handler 的 handleMessage() 处理消息。
Message 是 Android 异步消息处理机制中的一个关键类,位于 android.os 包下。它用于在 Handler 和 Looper 的协作下封装和传递消息。简单来说,Message 对象用于携带你希望传递的信息或任务数据,可以包含以下内容:
- what:代表消息类型的整型标识符,通常用来区分不同的消息。
- arg1 和 arg2:可以传递两个整型数据。
- obj:可以用来传递任何对象,通常用于携带复杂数据。
- target:用于记录接收消息的 Handler,一般不需要手动设置,由 Handler 自己处理。
当你调用 Handler 的 sendMessage() 或 sendEmptyMessage() 方法时,就会将 Message 对象放入绑定的 MessageQueue 中,然后由相应线程的 Looper 循环取出并分发给 Handler 的 handleMessage() 方法进行处理。
──────────────────────── 【示例说明】
下面提供一个完整的示例,使用 HandlerThread 创建一个后台线程,并通过 Handler 进行异步消息传递,模拟耗时任务处理后将结果返回给 UI 线程更新界面。
这个示例包含两个 Handler:
- backgroundHandler:运行在后台线程,用于处理耗时任务;
- uiHandler:运行在主线程,用于更新 UI。
在 MainActivity 中首先启动 HandlerThread,该线程就会有自己的 Looper,然后利用 Handler 与该 Looper 绑定创建 backgroundHandler。异步任务完成后,再将结果通过 uiHandler 发回主线程进行界面更新。
──────────────────────── 【完整示例】
Main.kt:
package com.example.myapplication
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
/*
MSG_PROCESS_TASK = 1
表示“处理任务”的消息类型。当 Handler 接收到这种消息时,就会执行与“开始执行任务”相关的逻辑,比如在后台线程中执行耗时任务。
MSG_TASK_RESULT = 2
表示“任务结果”的消息类型。当 Handler 接收到这种消息时,就会处理任务完成后的结果,比如在主线程中更新 UI。
*/
companion object {
const val MSG_PROCESS_TASK = 1
const val MSG_TASK_RESULT = 2
}
/*
andlerThread 是 Android 提供的一个线程类,它在创建线程的同时自动为该线程初始化了 Looper 和消息队列
这样你就可以在这个线程上创建一个 Handler,通过这个 Handler 向该线程的消息队列中发送消息或任务
而不需要手动调用 Looper.prepare() 和 Looper.loop()
这对于需要在后台线程上执行一系列顺序任务、并且需要异步处理消息的场景非常适用。
*/
// HandlerThread 用于创建后台线程,该线程拥有自己的 Looper 和消息队列
private lateinit var handlerThread: HandlerThread
// Handler 绑定到后台线程的 Looper,处理耗时任务
private lateinit var backgroundHandler: Handler
// Handler 绑定到主线程的 Looper,用于更新 UI
private lateinit var uiHandler: Handler
private lateinit var textViewResult: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textViewResult = findViewById(R.id.textViewResult)
// 初始化 HandlerThread,并启动线程
/*
启动一个叫做 "BackgroundThread" 的后台线程,该线程具备消息处理能力,可以用来执行耗时任务
然后通过 Handler 将任务的处理结果发送回主线程(或其他线程)更新 UI 或进一步处理。
启动 HandlerThread 的作用就是为接下来要在这个后台线程中创建 Handler 并处理消息(例如执行耗时任务)做好准备。
*/
handlerThread = HandlerThread("BackgroundThread")
handlerThread.start()
// 初始化 backgroundHandler,并绑定到 HandlerThread 的 Looper 上
/*
我们使用 Kotlin 的匿名对象语法来创建一个 Handler 的子类实例。具体来说,它完成了以下工作:
它使用 handlerThread.looper,即之前由 HandlerThread 启动的后台线程的 Looper
这意味着这个 Handler 会将接收到的所有消息和任务在后台线程中处理,而不会阻塞主线程。
通过匿名对象的方式创建了 Handler 的子类,并重写了其 handleMessage() 方法
从而定义了如何处理发送到该 Handler 的消息。
when (msg.what) 是一个条件分支语句,用来根据 msg.what 的值来决定执行哪段代码。它类似于其他语言中的 switch-case 语句。
在 Android 的 Message 类中,what 是一个整数属性,用来标识消息的类型。你可以把它看作是一种“标签”或“标识符”
用于在 Handler 的 handleMessage 方法中区分不同的消息,从而决定执行哪一段代码逻辑。
Message.obtain() 是一种创建 Message 对象的推荐方式,它会检查消息池中是否有可重用的 Message 实例
这样可以减少内存分配和 GC(垃圾回收)的开销。获得 Message 对象后
你可以设置它的各种属性(例如 what、obj、arg1、arg2 等),然后将其发送到 Handler 中,以便后续处理。
•what:这是一个整型(Int)的字段,用于标识消息的类型。你可以把它看作是一种“标签”,通过设置不同的值来区分消息的含义
例如,当收到消息时,你就可以通过判断 msg.what 来决定执行哪种操作。
• obj:这是一个 Object 类型的字段,可以存放任意对象。它用于传递额外的数据
例如,你可以把一个字符串、一个数据对象或者其他需要传输的数据赋给 obj
然后在接收方(比如 Handler 的 handleMessage 方法中)取出来使用。
*/
backgroundHandler = object : Handler(handlerThread.looper) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_PROCESS_TASK -> {
// 执行耗时任务
val result = performLongRunningTask()
// 创建 Message 对象传递任务结果到 UI 线程
val resultMsg = Message.obtain()
resultMsg.what = MSG_TASK_RESULT
resultMsg.obj = result
// 发送消息给 uiHandler 更新界面
uiHandler.sendMessage(resultMsg)
}
}
}
}
// 初始化 uiHandler,并绑定到主线程的 Looper 上
/*
Looper.getMainLooper() 返回的是 Android 应用中主线程(UI 线程)上的 Looper 对象
Looper 是一个消息循环,它不断地从消息队列中取出消息并分发给相应的 Handler 处理
由于 Android 要求所有的 UI 相关操作必须在主线程中执行
所以当你需要在主线程更新 UI 或处理界面相关的任务时,就会用到主线程的 Looper。
*/
uiHandler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_TASK_RESULT -> {
val result = msg.obj as String
textViewResult.text = "Task Result: $result"
}
}
}
}
// 模拟发送消息到后台线程开始任务的处理
//(这个里面传的参数就是Message)
backgroundHandler.sendEmptyMessage(MSG_PROCESS_TASK)
}
// 模拟耗时任务方法
private fun performLongRunningTask(): String {
// 模拟延时2秒
Thread.sleep(2000)
return "Task Completed"
}
override fun onDestroy() {
super.onDestroy()
// 关闭 HandlerThread,防止内存泄漏
handlerThread.quitSafely()
}
}
──────────────────────── 【对应布局文件】
下面是简单的布局文件 activity_main.xml,其中包含一个 TextView 用于显示任务处理结果。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- 显示后台任务处理后的结果 -->
<TextView
android:id="@+id/textViewResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Waiting for task..."
android:textSize="18sp"
android:layout_centerInParent="true"/>
</RelativeLayout>
──────────────────────── 【详细说明】
-
HandlerThread 的使用:
- HandlerThread 是 Android 提供的一个方便的工具,用于创建一个具有 Looper 的后台线程。调用 start() 方法后,HandlerThread 会启动并初始化自己的消息队列。
-
backgroundHandler 的创建:
- 通过传入 handlerThread.looper 使得 backgroundHandler 绑定到刚创建的后台线程上。调用 sendEmptyMessage(MSG_PROCESS_TASK) 即把消息加入后台线程的消息队列,由该 Handler 的 handleMessage() 方法处理。
-
uiHandler 的创建:
- 使用 Looper.getMainLooper() 绑定到主线程。该 Handler 接收到消息后会在主线程中运行,从而安全更新 UI。
-
Message 的使用:
- 使用 Message 对象传递数据(例如耗时任务结果)。在 backgroundHandler 中,处理完任务后构造一个 Message,并调用 uiHandler.sendMessage() 方法将消息发送到主线程处理。
通过这种机制,Android 实现了线程间的有效通信,既能在后台线程中执行耗时操作,又能安全地在主线程中更新界面。这样既避免了直接在子线程更新 UI 导致的异常,又实现了任务的异步处理。
给个思维导图: