盒子
盒子
文章目录
  1. 什么是Handler?
  2. Handler的使用方式
    1. 创建Handler对象
    2. 发送消息
    3. 处理消息
  3. Handler的原理
    1. 消息队列和Looper
    2. 消息分发
  4. 总结
  5. 推荐

Handler真的难?看完这篇文章你就懂了!

在Android开发中,Handler是一个非常重要的组件,它可以用来实现线程之间的通信和任务调度。本篇文章将介绍Handler的使用方式和原理,帮助读者更好地理解Android开发中的线程处理。

什么是Handler?

Handler是Android中的一个消息处理器,它可以接收并处理其他线程发来的消息。简单来说,Handler就是一个用来处理消息的工具类,它可以将消息发送给其他线程,也可以接收其他线程发送的消息进行处理。

Handler的使用方式

使用Handler的基本流程为:创建Handler对象 -> 发送消息 -> 处理消息。

在使用 Handler 之前,需要了解一些相关概念:

  • 线程:是独立运行的程序段,执行的代码是一个单独的任务。
  • 消息队列:是一种存储消息的数据结构,支持先进先出的队列操作。
  • Looper:可以让线程不停地从消息队列中取出消息并处理,是线程与消息队列交互的桥梁。
  • Message:是 Android 中处理消息的基本类,可以携带一些数据,用于在 Handler 中进行处理。

创建Handler对象

在使用Handler之前,需要先创建一个Handler对象。创建Handler对象的方式有两种:

  • 在主线程中创建Handler对象:

    在主线程中创建Handler对象非常简单,只需要在主线程中创建一个Handler对象即可:

    1
    Handler handler = new Handler();
  • 在子线程中创建Handler对象:

    在子线程中创建Handler对象需要先获取到主线程的Looper对象,然后使用Looper对象来创建Handler对象:

    1
    Handler handler = new Handler(Looper.getMainLooper());

发送消息

创建Handler对象之后,就可以使用它来发送消息了。发送消息的方式有两种:

  • 使用Handler的post()方法:

    使用Handler的post()方法可以将一个Runnable对象发送到Handler所在的消息队列中。Runnable对象中的代码会在Handler所在的线程中执行。

    1
    2
    3
    4
    5
    6
    handler.post(new Runnable() {
    @Override
    public void run() {
    // 在Handler所在的线程中执行的代码
    }
    });
  • 使用Handler的sendMessage()方法:

    使用Handler的sendMessage()方法可以将一个Message对象发送到Handler所在的消息队列中。Message对象中可以携带一些数据,用于在Handler中进行处理。

    1
    2
    3
    4
    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所在的线程中执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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所在的线程中执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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) 的功能。

1
2
3
4
5
6
7
8
9
10
11
12
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 的方法向消息队列中发送消息。

1
2
3
4
5
6
7
Handler handler = new Handler() ;
handler.post(new Runnable(){
@Override
public void run() {
// 在其他线程发送消息
}
});

1.2. 创建 Message 对象

将需要传输的数据封装成 Message 类型的对象,然后将该对象塞入消息队列中。

1
2
3
Message msg = new Message();
msg.obj = "消息内容";
handler.sendMessage(msg);

1.3. 将消息加入消息队列

Handler 将消息放入消息队列中。

在 Handler 内部,新构建的消息通过 enqueueMessage() 方法被加入到 MessageQueue 相应的内存块中,并且会在该内存块的标记 next 表示下一个内存块的索引号。

1
2
3
4
5
6
7
8
9
10
11
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() 方法的核心逻辑,就是紧接着找到消息队列中最近的一个时间戳比当前时间小的消息,将新消息插入到这个消息之后。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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秒钟来启动上下文中的消息循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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() 方法中有这样一行代码:

1
msg.target.dispatchMessage(msg);

1.6. 传递 Message 对象

从消息中获取到 target 属性,它就是当前这个Message对象所属的 Handler,并执行该Handler的 handleMessage(Message) 方法。

dispatchMessage(Message) 的核心代码是判断 Message.target 是否为 null,不为 null 则将消息传递给目标 Handler,如果为 null,则直接抛出异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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: 每日一算法,由浅入深,欢迎加入一起共勉。

支持一下
赞赏是一门艺术