Android Handler 消息机制

每一个搞 Android 的对 Handler 应该都不陌生,网上大神的文章也是数不尽的。这篇文章是个人在学习 Handler 过程中的一些疑问的记录。

1. 什么是 Handler,有什么作用

Handler 首先是一个 java 类,定义在/frameworks/base/core/java/android/os/Handler.java。但一般提到 Handler 并不专指它自身,常用来代指 Android 中的消息处理机制,或者说线程间通信的工具,典型应用就是在子线程中更新 UI 线程。那么,很显然 Handler 的作用就是用于线程间通信。

2. Android 消息机制的四要素

Message:需要传递的消息,通信的过程不就是消息传递的过程嘛。
MessageQueue:消息队列是消息保存的容器,收到的消息会首先进入 MessageQueue 中,等待被取出后处理。
Handler:负责消息的发送和处理,可见 Handler 的重要地位。
Looper:负责消息分发,会不断尝试从 MessageQueue 中取出消息,交给对应的 Handler 来处理。

3. 四要素间是如何协调工作的

首先要知道以下几点:

  1. 主线程有且仅有一个 Looper,Looper 内部持有一个 MessageQueue 类对象,也就是说 MessageQueue 其实是封装在 Looper 内部的,也是唯一的。
  2. 一个 Looper 可以对应多个 Handler,负责在不同子线程中发送和处理消息,它们发送的消息都会到主线程中的 Messagequeue 中来,并最终被 Looper 派发到对应子线程中的 Handler 来处理。
  3. 一个 Handler 只能对应一个 looper。Handler 在创建的时候会绑定 Looper,如果没有指定就是当前线程对应的 Looper,handleMessage 的回调是在 Looper 所在的线程。

这里直接放一张郭神博客里 Handler 工作流程图

在这里插入图片描述
我们以更新 UI 的场景为例,描述一下运行的过程。

  1. 首先在主线程中创建 Handler 对象 handler1,创建后该 Handler 就会与主线程的 Looper 绑定;
  2. 创建子线程,并在子线程中执行某些 UI 相关的任务,任务执行完后在子线程中调用 handler1.sendMessage,更新 UI 的消息 message1 随后会进入到主线程的 messageQueue 中;
  3. Looper 处理到 message1 时,会将该消息派发到对应的 Handler(handler1)进行处理,回调到它的 handleMessage 方法,完成 UI 的更新。

这个过程说起来很简单,但其中的一些细节还是比较让人困惑,我们接着一起看。

4. Handler 创建时是怎么绑定 Looper 的

我们不妨一起来看一下 Handler 这个类。它维护有一个 mLooper 和 mQueue,分别用来记录该 Handler 对应的 Looper 和 MessageQueue。

942      final Looper mLooper;
943      final MessageQueue mQueue;

下面看一下这两个量的赋值,也就是 Handler 的初始化方法。

         //不指定 Looper
128      public Handler() {
129          this(null, false);
130      }

         //指定 Looper
161      public Handler(@NonNull Looper looper) {
162          this(looper, null, false);
163      }

214      public Handler(@Nullable Callback callback, boolean async) {
             //获取当前线程的 Looper 对象
224          mLooper = Looper.myLooper();
230          mQueue = mLooper.mQueue;
...
233      }

255      public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
             //使用指定的 looper
256          mLooper = looper;
257          mQueue = looper.mQueue;
260      }

从 Handler 的构造方法,我们不难发现。当未指定 Looper 时,Handler 将绑定创建它的线程的 Looper 对象;而如果指定了,mLooper 则赋值为指定的 Looper 对象。mQueue 始终都是绑定的 Looper 对应的 MessageQueue。

下面是 Looper.myLooper() 的定义

307      /**
308       * Return the Looper object associated with the current thread.  Returns
309       * null if the calling thread is not associated with a Looper.
310       */
311      public static @Nullable Looper myLooper() {
312          return sThreadLocal.get();
313      }

5. 子线程中消息是怎么发送的

哈哈,当然是调用 Handler 的 send 和 post 相关方法啊。哦,说的有点概况了,具体调用哪个方法很灵活,但最终发送消息一定会调用到sendMessageAtTime()

719      public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
720          MessageQueue queue = mQueue;
721          if (queue == null) {
722              RuntimeException e = new RuntimeException(
723                      this + " sendMessageAtTime() called with no mQueue");
724              Log.w("Looper", e.getMessage(), e);
725              return false;
726          }
727          return enqueueMessage(queue, msg, uptimeMillis);
728      }
729  

这里指定 queue 是 mQueue 了,也就是 Looper 对应线程的 MessageQueue, 在 enqueueMessage() 方法中主要就是做入队操作了。留意一下 msg.target = this,我们后面还会提到。

770      private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
771              long uptimeMillis) {
772          msg.target = this;
773          msg.workSourceUid = ThreadLocalWorkSource.getUid();
774  
775          if (mAsynchronous) {
776              msg.setAsynchronous(true);
777          }
778          return queue.enqueueMessage(msg, uptimeMillis);
779      }

6. Looper 是怎么派发消息到对应 Handler 的

Looper 在创建后,当调用了 Looper.loop() 就会开始不断尝试从 MessageQueue 中获取 Message , 并分发给对应的 Handler,所以看一下 Looper.loop()

260      public static void loop() {
261          final Looper me = myLooper();
262          if (me == null) {
263              throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
...
287          for (;;) {
288              if (!loopOnce(me, ident, thresholdOverride)) {
289                  return;
290              }
291          }
292      }
155      /**
156       * Poll and deliver single message, return true if the outer loop should continue.
157       */
158      @SuppressWarnings("AndroidFrameworkBinderIdentity")
159      private static boolean loopOnce(final Looper me,
160              final long ident, final int thresholdOverride) {
161          Message msg = me.mQueue.next(); // might block
162          if (msg == null) {
163              // No message indicates that the message queue is quitting.
164              return false;
165          }
...
200          try {
                 //target 就是一个 Handler 类对象
201              msg.target.dispatchMessage(msg);
...
206          } 
...
250          msg.recycleUnchecked();
251  
252          return true;
253      }

在上一节我们就有说到 msg.target,它是什么,我们到 Message.java 中一探究竟。
在这里插入图片描述
哦,原来就是一个 Handler 类对象,这就很容易理解了。每一个 Message 在发送的时候就赋值了 target 的值,在派发的时候就只需要根据 target 调用 dispatchMessage() 方法就行了

97      public void dispatchMessage(@NonNull Message msg) {
98          if (msg.callback != null) {
99              handleCallback(msg);
100          } else {
101              if (mCallback != null) {
102                  if (mCallback.handleMessage(msg)) {
103                      return;
104                  }
105              }
106              handleMessage(msg);
107          }
108      }

handleMessage() 就是我们在开始创建 Handler 时重写的方法,在这里被最终调用。

7. 如何保证一个线程只有一个 Looper

答案藏在 Looper 的 Looper.prepare()中,也就是将当前线程初始化为 Looper 线程的过程中。

108      public static void prepare() {
109          prepare(true);
110      }
111  
112      private static void prepare(boolean quitAllowed) {
113          if (sThreadLocal.get() != null) {
114              throw new RuntimeException("Only one Looper may be created per thread");
115          }
116          sThreadLocal.set(new Looper(quitAllowed));
117      }

除非调用过 prepare(), 否则 sThreadLocal.get() 将会返回 null,这就保证了 Looper 的构造方法只会调用一次,保证 Looper 的唯一性。

70      // sThreadLocal.get() will return null unless you've called prepare().
71      @UnsupportedAppUsage
72      static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

这里还有个疑问,创建的 Looper 为何要保存到 ThreadLocal 中。那得先说说 ThreadLocal 了,它是线程内部的数据存储类(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。相应区域数据改变只与当前线程关联,线程之间互不干扰。这就能保证每个线程的 Looper 都是独立使用的。

以上就是我个人在学习 Handler 过程的主要疑问与心得了,欢迎大家一起讨论。最后贴上某位大神整理的 Handler 架构图,方便大家理解记忆。原图出处在此
在这里插入图片描述

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值