82.解析异步消息处理机制

异步消息处理机制主要由 Message、Handler、Looper 以及 MessageQueue 构成,它们共同协作实现线程间的通信与任务调度。

─────────────────────── 【基本概念】

  1. Looper 与 MessageQueue

    • 每个线程可以拥有一个 Looper。Looper 会不断循环,从与之相关联的 MessageQueue 中取出消息逐一分发和处理。
    • 主线程默认已经有 Looper,即主 Looper;而其他线程需要通过 HandlerThread 或手动调用 Looper.prepare() 和 Looper.loop() 来创建。

Looper 是 Android 异步消息处理机制中的核心组件之一。它主要负责以下功能:

  1. 管理消息队列(MessageQueue):Looper 会不断循环,从消息队列中取出 Message 消息,并将这些消息分发给相应的 Handler 处理。

  2. 在特定线程中执行消息循环:每个线程至多拥有一个 Looper,该 Looper 控制着该线程的消息循环。Android 的主线程(UI 线程)在应用启动时就已经存在一个默认的 Looper,可以通过 Looper.getMainLooper() 获取。

  3. 维持线程等待并处理异步任务:通过与 Handler 配合使用,Looper 能让线程在没有任务时处于等待状态,一旦有消息到来就进行处理。这种机制可以让线程不断地处理异步任务,而不是不断轮询或空转。

Looper 是 Android 中实现异步消息处理的关键组件,它使得线程能够便捷地进行任务调度和消息处理,而无需自己管理复杂的线程间同步问题。

  1. Handler

    • Handler 负责将消息发送到指定线程的 MessageQueue 中,并定义如何处理这些消息。
    • 当 Handler 与一个 Looper 绑定后,Handler 的 handleMessage() 方法就是在该线程中运行的。

Handler 是 Android 异步消息处理机制中的核心组件,它主要起到以下作用:

  1. 将消息或任务(Runnable)加入到与特定线程关联的消息队列中。
  2. 分发这些消息或任务到对应线程,使其在该线程的上下文中执行。

在 Android 中,每个 Handler 都关联一个 Looper 和它的消息队列。通过 Handler,你可以在任何线程中发送消息或任务,并由绑定的线程的 Looper 进行循环处理。通常,我们在主线程中使用 Handler 来安排 UI 更新,也可以在子线程中使用 Handler 执行后台任务。

  1. 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>

──────────────────────── 【详细说明】

  1. HandlerThread 的使用:

    • HandlerThread 是 Android 提供的一个方便的工具,用于创建一个具有 Looper 的后台线程。调用 start() 方法后,HandlerThread 会启动并初始化自己的消息队列。
  2. backgroundHandler 的创建:

    • 通过传入 handlerThread.looper 使得 backgroundHandler 绑定到刚创建的后台线程上。调用 sendEmptyMessage(MSG_PROCESS_TASK) 即把消息加入后台线程的消息队列,由该 Handler 的 handleMessage() 方法处理。
  3. uiHandler 的创建:

    • 使用 Looper.getMainLooper() 绑定到主线程。该 Handler 接收到消息后会在主线程中运行,从而安全更新 UI。
  4. Message 的使用:

    • 使用 Message 对象传递数据(例如耗时任务结果)。在 backgroundHandler 中,处理完任务后构造一个 Message,并调用 uiHandler.sendMessage() 方法将消息发送到主线程处理。

通过这种机制,Android 实现了线程间的有效通信,既能在后台线程中执行耗时操作,又能安全地在主线程中更新界面。这样既避免了直接在子线程更新 UI 导致的异常,又实现了任务的异步处理。

给个思维导图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值