• Android Handler深入学习(源码分析)


    目录:

     

    1. 背景

    在分析源码之前,先来了解一下Message、MessageQueue、Looper这几个对象。

    1.1 Message 消息

    定义:是线程间通讯的数据单元,包含着描述信息及任意数据对象,发送到 Handler。

    在实际使用中,我们在工作线程中通过 Handler.sendMessage(Message),将携带数据的 Message 信息发送给 Handler,然后再由 Handler 处理,根据不同的信息,通知主线程作出相对应的 UI 工作。

    官方文档说明:

    1. /**
    2. * 定义包含着描述信息及任意数据对象的可发送到 Handler 的信息。额外包含可以不被分配的两个 int 字段及一个 Object 字段。
    3. * 获取 Message 对象最好的方法是调用 Message.obtain() 或 调用 Handler.obtainMessage() 方法来获取,调用该方法将从可回收的对象池中获取对象。
    4. */
    5. public final class Message implements Parcelable {
    6. /**
    7. * 用户定义的消息代码,以便接收者能够识别此消息。每个 Handler 都有自己的消息代码命名空间,因此不必担心你的 Handler 与其他 Handler 起冲突。
    8. */
    9. public int what;
    10. /**
    11. * 如果只需要存储几个整数值,以 arg1 和 arg2 为变量去使用 setData(Bundle) 是较低成本的替代选择方法。
    12. */
    13. public int arg1;
    14. public int arg2;
    15. /**
    16. * 发送给接收者的任意对象,当使用 Messager 跨进程发送消息时,如果包含着可序列化的框架类时,Message必须是非空的。使用setData()来传输数据。
    17. * 注意,Android 2.2发行版之前不支持此处的Parcelable对象。
    18. */
    19. public Object obj;
    20. ···略···
    21. 复制代码

    1.2 MessageQueue 消息队列

    定义:用来存储 Message 的数据队列。

    官方文档说明:

    1. /**
    2. * 包含着一系列由 Looper 分发的 Message 的一个低级类。
    3. * Message 不是直接添加到 MessageQueue 中的,而是通过与 Looper 关联的 Handler 对象来添加的。
    4. * 你可以使用“loop.myQueue()”方法来获取当前线程的 MessageQueue 对象。
    5. */
    6. public final class MessageQueue {
    7. private static final String TAG = "MessageQueue";
    8. ···略···
    9. }
    10. 复制代码

    1.3 Looper 消息循环器

    定义:用于为线程执行消息循环的一个类。是 MessageQueue 与 Handler 之间的通讯媒介。

    官方文档说明:

    1. /**
    2. * 用于为线程执行消息循环的一个类。
    3. * 线程默认情况下没有与之关联的消息循环;要创建一个,在将要运行循环的线程中调用 Looper.prepare(),然后调用 Looper.loop() 让它处理消息,直到循环停止。
    4. * 与消息循环的大多数交互都是通过 Handler 类进行的。
    5. * 这是一个典型的 Looper 线程实现的例子,使用 Looper.prepare() 和 Looper.loop() 方法创建一个 Handler 对象与 Looper 进行通信。
    6. *
    7. * class LooperThread extends Thread {
    8. * public Handler mHandler;
    9. *
    10. * public void run() {
    11. * Looper.prepare();
    12. *
    13. * mHandler = new Handler() {
    14. * public void handleMessage(Message msg) {
    15. * // 在这里处理传入的消息
    16. * }
    17. * };
    18. *
    19. * Looper.loop();
    20. * }
    21. * }
  • */
  • public final class Looper {
  • /*
  • * API 实现注意事项:
  • *
  • * 该类包含设置和管理基于 MessageQueue 的事件循环所需的代码。
  • * 影响队列状态的api应该在 MessageQueue 或 Handler 上定义,而不是在 Looper 本身上定义。
  • * 例如,在队列上定义空闲处理程序和同步屏障,而在 Looper 上定义线程准备、循环和退出。
  • */
  • private static final String TAG = "Looper";
  • ···略···
  • }
  • 复制代码
  • 1.4 Message、MessageQueue、Looper之间的关系

    一句话概括: 存储在 MessageQueue 中的 Message 被 Looper 循环分发到指定的 Handler 中进行处理。

    2. Handler 通信机制原理

    关于Handler的通信机制工作原理,请看 Carson_Ho大佬的 Android Handler:图文解析 Handler通信机制 的工作原理 写的超棒,图文解析,一目了然。引用其中一Handler通信流程示意图,如下:

     

    Thread、Handler、Looper三者之间的数量对应关系;

    • 一个 Thread 可以有多个 Handler。
    • 一个 Handler 只能关联一个 Looper 对象。
    • 反之,一个 Looper 可以被多个 Handler 所关联。

    3. 源码分析

    针对 Handler.sendMessage(msg) 方法展开分析,分为3步:

    1. 创建 Handler 对象
    2. 创建 Message 对象
    3. 分发 Message

    3.1 创建 Handler 对象

    创建一个 Handler 对象,也就是实例化一个 Handler 对象,在实际的使用中,我也发现有存在三种情况,分别是:

    1. 在主线程上新建一个 Handler 对象,提供应用程序的 主Looper 对象与之绑定关联。
    2. 创建一个继承于 Handler类的静态内部类,防止内存泄漏。
    3. 在子线程上创建 Handler 对象。

    我将一一展开分析:

    3.1.1 在主线程上新建一个 Handler 对象,提供应用程序的 主Looper 对象与之绑定关联

    如下,我们在主线程中新建 Handler 对象,

    1. //在主线程中新建 Handler 对象,并提供应用程序的 主Looper
    2. mHandler = new Handler(Looper.getMainLooper()) {
    3. @Override
    4. public void handleMessage(Message msg) {
    5. super.handleMessage(msg);
    6. //消息处理
    7. }
    8. };
    9. 复制代码

    通过上述代码,我们创建了一个 Handler 对象,并关联应用程序的 主Looper 对象。 这个时候我们看一下 Handler类 的构造函数,如下所示:

    1. /**
    2. * Use the provided {@link Looper} instead of the default one.
    3. * 使用提供的 Looper对象, 而不是使用默认的Looper对象
    4. * @param Looper 不能为 null
    5. */
    6. public Handler(@NonNull Looper looper) {
    7. this(looper, null, false);
    8. }
    9. 复制代码

    可以发现,我们提供给 Handler 的 Looper 是通过 Looper.getMainLooper() 获取的。往下看:

    1. /**
    2. * 返回应用程序的 主Looper, 它存在于应用程序的主线程中。
    3. */
    4. public static Looper getMainLooper() {
    5. synchronized (Looper.class) {
    6. //返回sMainLooper, 下一步看看sMainLooper是怎么创建 Looper 对象的。
    7. return sMainLooper;
    8. }
    9. }
    10. 如上所诉,getMainLooper() 返回 全局变量 sMainLooper 对象,所以我们需要看 sMainLooper 是什么时候被赋值的,如下所示,在 prepareMainLooper() 方法中,sMainLooper 被 myLooper 赋值:
    11. /**
    12. * 将当前线程初始化为 Looper,并将其标记为应用程序的 主Looper
    13. * 应用程序的 主Looper 是由 Android环境 创建的,所以永远不需要自己调用这个函数
    14. */
    15. public static void prepareMainLooper() {
    16. //调用 prepare() 方法,如果当前线程没有 Looper 对象,就为之新创建一个 Looper 对象。
    17. prepare(false);
    18. synchronized (Looper.class) {
    19. if (sMainLooper != null) {
    20. throw new IllegalStateException("The main Looper has already been prepared.");
    21. }
    22. //返回当前线程所关联的 Looper 对象。
    23. sMainLooper = myLooper();
    24. }
    25. }
    26. 上一步 prepareMainLooper 中:调用了 prepare(false) 来创建 Looper 对象,具体如下:
    27. private static void prepare(boolean quitAllowed) {
    28. //sThreadLocal.get() 返回 Looper || null,如果返回 Looper 对象,即代表已经为该线程创建了 Looper 对象,
    29. //从而抛出异常,提示:“每个线程只能创建一个Looper”
    30. if (sThreadLocal.get() != null) {
    31. throw new RuntimeException("Only one Looper may be created per thread");
    32. }
    33. //否则,调用 new Looper()方法新建一个 Looper 对象,并放入 sThreadLocal 变量中。
    34. sThreadLocal.set(new Looper(quitAllowed)); //quitAllowed = false
    35. }
    36. 上一步 prepare() 中:调用了 new Looper() 方法来新创建 Looper 对象,具体如下:
    37. private Looper(boolean quitAllowed) {
    38. //新建一个 MessageQueue
    39. mQueue = new MessageQueue(quitAllowed);
    40. mThread = Thread.currentThread();
    41. }
    42. 在 prepareMainLooper() 方法中,如果 sMainLooper 为 null,就赋值 sMainLooper = myLooper(); , myLooper() 方法如下:
    43. /**
    44. * 返回当前线程所关联的 Looper 对象,如果所调用的线程没有关联的 Looper 对象,就返回 null
    45. */
    46. public static @Nullable Looper myLooper() {
    47. //获取的 Looper 对象,是在 prepare() 方法中通过 sThreadLocal.set(new Looper(quitAllowed)); 所创建的
    48. return sThreadLocal.get();
    49. }
    50. 复制代码

    上述代码中,getMainLooper() 方法中的 sMainLooper 是通过 prepareMainLooper() 进行赋值的,具体代码分析如上所述,其流程图如下所示:

     

    3.1.2 创建一个继承于 Handler类的静态内部类,防止内存泄漏

    上述创建 Handler 对象的方法是 new Handler(Looper.getMainLooper()) ,通过 Looper.getMainLooper() 方法获取 Looper 对象,然后传入到 Handler 的构造函数中,构成绑定关联关系。 但在实际开发中,我们通常将 Handler 写成静态内部类的形式,如下:

    1. //创建 Handler 对象
    2. mHandler = new MyHandler(this);
    3. /**
    4. * 将 Handler 写成静态内部类,防止内存泄露
    5. */
    6. public static class MyHandler extends Handler {
    7. WeakReference<HandlerAddThreadActivity> weakReference;
    8. public MyHandler(HandlerAddThreadActivity activity) {
    9. weakReference = new WeakReference<>(activity);
    10. }
    11. @Override
    12. public void handleMessage(Message msg) {
    13. super.handleMessage(msg);
    14. //处理收到的信息
    15. }
    16. }
    17. 复制代码

    思考: 疑问?根据上面的代码,我们没有传递 Looper 对象给 Handler 去绑定关联, 那 Handler 为何又能正常工作? 带着疑惑上路:

    1. 首先看一下 Handler类 的默认构造函数
    2. /**
    3. * 默认构造函数将这个处理程序与当前线程的{@link Looper}关联起来。
    4. * 如果此线程没有looper,则此处理程序将无法接收消息,因此将引发异常。
    5. */
    6. public Handler() {
    7. this(null, false);
    8. }
    9. public Handler(@Nullable Callback callback, boolean async) {
    10. ···略···
    11. //返回与当前线程关联的Looper对象。如果调用线程没有关联的 Looper 对象,则返回null
    12. mLooper = Looper.myLooper();
    13. //如果 mLooper 为null,则抛出异常。
    14. if (mLooper == null) {
    15. throw new RuntimeException(
    16. "Can't create handler inside thread " + Thread.currentThread()
    17. + " that has not called Looper.prepare()");
    18. }
    19. //获取该 Looper 的 MessageQueue
    20. mQueue = mLooper.mQueue;
    21. //callback = null && async = false
    22. mCallback = callback;
    23. mAsynchronous = async;
    24. }
    25. 看着不明显,mLooper 是通过 Looper.myLooper() 方法获得的.
    26. public static @Nullable Looper myLooper() {
    27. return sThreadLocal.get();
    28. }
    29. 但这个时候我想起了 prepareMainLooper() 方法。
    30. /**
    31. * 将当前线程初始化为 Looper,并将其标记为应用程序的 主Looper
    32. * 应用程序的 主Looper 是由 Android环境 创建的,所以永远不需要自己调用这个函数
    33. */
    34. public static void prepareMainLooper() {
    35. //调用 prepare() 方法,如果当前线程没有 Looper 对象,就为之新创建一个 Looper 对象。
    36. prepare(false);
    37. synchronized (Looper.class) {
    38. if (sMainLooper != null) {
    39. throw new IllegalStateException("The main Looper has already been prepared.");
    40. }
    41. //返回当前线程所关联的 Looper 对象。
    42. sMainLooper = myLooper();
    43. }
    44. }
    45. 疑问解除,无论我们有没有手动添加 Looper 给 Handler, Android环境都会为我们自动创建一个主线程的主Looper对象。
    46. 复制代码

    总结: 我们创建的 MyHandler 静态内部类,其实也是在主线程上创建的, Android环境会为我们自动创建一个应用程序的 主Looper 对象,主线程与 主Looper 绑定关联。

    3.1.3 在子线程上创建 Handler 对象

    ok, 解除了刚刚的疑问,我又问自己,那在子线程创建 Handler 对象呢? 当然,如果你在子线程中新创建一个 Handler 对象,创建的方法为:

    1. class LooperThread extends Thread {
    2. public Handler mHandler;
    3. public void run() {
    4. //为当前线程创建一个 Looper
    5. Looper.prepare();
    6. //在子线程中创建 Handler 对象
    7. mHandler = new Handler() {
    8. public void handleMessage(Message msg) {
    9. // 在这里处理传入的消息
    10. }
    11. };
    12. //开始消息循环
    13. Looper.loop();
    14. }
    15. }
    16. 复制代码

    在子线程上,我们通过调用 prepare() 来为当前线程创建一个 Looper 对象

    1. /** 将当前线程初始化为 Looper 对象,在实际开始循环之前创建并引用该 Looper 对象。
    2. * 请确保在调用此方法后调用 loop() 方法,并通过调用 quit() 结束它
    3. */
    4. public static void prepare() {
    5. prepare(true);
    6. }
    7. private static void prepare(boolean quitAllowed) {
    8. if (sThreadLocal.get() != null) {
    9. throw new RuntimeException("Only one Looper may be created per thread");
    10. }
    11. sThreadLocal.set(new Looper(quitAllowed));
    12. }
    13. 复制代码

    并且在 Handler 与 Looper 构成绑定关联关系后,通过调用 Looper.loop() 方法开始消息循环。

    3.2 创建 Message消息对象

    关于创建 Message 对象有两种创建方法,分别是:

    1. Message myMessage = new Message();
    2. Message myMessage = Message.obtain()
    1. /**
    2. * 从全局池返回一个新的消息实例。允许我们在许多情况下避免分配新对象。
    3. */
    4. public static Message obtain() {
    5. synchronized (sPoolSync) {
    6. if (sPool != null) {
    7. Message m = sPool;
    8. sPool = m.next;
    9. m.next = null;
    10. m.flags = 0; // clear in-use flag
    11. sPoolSize--;
    12. return m;
    13. }
    14. }
    15. return new Message();
    16. }
    17. 复制代码

    本文不过多展示。

    3.3 发送消息

    在工作线程中通过调用 mHandler.sendMessage(myMessage); 方法来发送消息,这一步是消息入消息队列操作,即将 Message 入 MessageQueue。

    1. /**
    2. * 在当前时间之前所有挂起的消息之后, 将消息推到消息队列的末尾。它将该线程所关联的 Handler 的 handleMessage 方法中被接收消息。
    3. * @return 如果信息成功加入到消息队列中,返回 true,反之,入队列失败则返回 false, 失败的原因通常是因为处理消息队列的“looper”正在退出。
    4. */
    5. public final boolean sendMessage(@NonNull Message msg) {
    6. return sendMessageDelayed(msg, 0);
    7. }
    8. /**
    9. * 延时 delayMillis 将消息入消息队列,它将该线程所关联的 Handler 的 handleMessage 方法中被接收消息。
    10. * @return 如果信息成功加入到消息队列中,返回 true,反之,入队列失败则返回 false, 失败的原因通常是因为处理消息队列的 Looper 正在退出。
    11. * 注意,true的结果并不意味着将处理该消息——如果 Looper 在消息的交付时间之前退出,则该消息将被删除。
    12. */
    13. public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    14. if (delayMillis < 0) {
    15. //默认 delayMillis = 0
    16. delayMillis = 0;
    17. }
    18. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    19. }
    20. /**
    21. * 在指定时间入队列,深度睡眠的时间会增加额外的执行延迟。它将该线程所关联的 Handler 的 handleMessage 方法中被接收消息。
    22. * @param uptimeMillis 发送消息的绝对时间,使用{SystemClock # uptimeMillis}获取。
    23. * @return 如果信息成功加入到消息队列中,返回 true,反之,入队列失败则返回 false, 失败的原因通常是因为处理消息队列的 Looper 正在退出。
    24. * 注意,true的结果并不意味着将处理该消息——如果 Looper 在消息的交付时间之前退出,则该消息将被删除。
    25. */
    26. public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    27. //获取当前 Looper 对象的消息队列
    28. MessageQueue queue = mQueue;
    29. //如果当前 Looper 对象没有消息队列,则抛出异常,返回 false, 代表消息入队列失败
    30. if (queue == null) {
    31. RuntimeException e = new RuntimeException(
    32. this + " sendMessageAtTime() called with no mQueue");
    33. Log.w("Looper", e.getMessage(), e);
    34. return false;
    35. }
    36. return enqueueMessage(queue, msg, uptimeMillis);
    37. }
    38. //将消息放入消息队列
    39. private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
    40. long uptimeMillis) {
    41. msg.target = this;
    42. msg.workSourceUid = ThreadLocalWorkSource.getUid();
    43. if (mAsynchronous) {
    44. msg.setAsynchronous(true);
    45. }
    46. return queue.enqueueMessage(msg, uptimeMillis);
    47. }
    48. 根据消息的处理时间入消息队列
    49. boolean enqueueMessage(Message msg, long when) {
    50. //获得当前的 Handler 对象,如果为 null, 则抛出异常
    51. if (msg.target == null) {
    52. throw new IllegalArgumentException("Message must have a target.");
    53. }
    54. //如果消息已经被使用,即已经入队列,则抛出异常
    55. if (msg.isInUse()) {
    56. throw new IllegalStateException(msg + " This message is already in use.");
    57. }
    58. synchronized (this) {
    59. //如果 Looper 已经退出,抛出异常,入队列失败,返回false
    60. if (mQuitting) {
    61. IllegalStateException e = new IllegalStateException(
    62. msg.target + " sending message to a Handler on a dead thread");
    63. Log.w(TAG, e.getMessage(), e);
    64. msg.recycle();
    65. return false;
    66. }
    67. msg.markInUse();
    68. msg.when = when;
    69. Message p = mMessages;
    70. boolean needWake;
    71. //如果队列里没有消息,或者新消息的处理时间排在最前,即作为消息队列新的队头插入消息队列
    72. if (p == null || when == 0 || when < p.when) {
    73. //新的队列头部,如果队列是 blocked 的状态则需要唤醒该事件队列。
    74. msg.next = p;
    75. mMessages = msg;
    76. needWake = mBlocked;
    77. } else {
    78. //插入处理时间插入到队列的中间。通常我们不需要唤醒事件队列,除非是队列头部的屏障,并且消息是队列中最早的异步消息
    79. Message prev;
    80. for (;;) {
    81. prev = p;
    82. p = p.next;
    83. if (p == null || when < p.when) {
    84. break;
    85. }
    86. if (needWake && p.isAsynchronous()) {
    87. needWake = false;
    88. }
    89. }
    90. msg.next = p; // invariant: p == prev.next
    91. prev.next = msg;
    92. }
    93. // We can assume mPtr != 0 because mQuitting is false.
    94. if (needWake) {
    95. nativeWake(mPtr);
    96. }
    97. }
    98. return true;
    99. }
    100. 复制代码

    3.4 消息循环,派发消息

    消息入 MessageQueue 队列后,由 Looper 循环消息队列,然后派发消息,如下所示:

    1. /**
    2. * 在线程中运行消息队列,并确保调用 quit() 方法来结束消息循环
    3. */
    4. public static void loop() {
    5. //获取当前线程关联的 Looper 对象,如果该线程没有关联的 Looper 对象,则抛出异常
    6. final Looper me = myLooper();
    7. if (me == null) {
    8. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    9. }
    10. //获取该 Looper 对象的消息队列
    11. final MessageQueue queue = me.mQueue;
    12. //循环消息队列
    13. for (;;) {
    14. //取出消息队列中的消息对象,如果消息队列为空,则 block 该线程。
    15. Message msg = queue.next(); // might block
    16. if (msg == null) {
    17. //没有消息则表明消息队列正在退出,解除消息循环
    18. return;
    19. }
    20. try {
    21. //消息对应的 Handler 分发消息
    22. msg.target.dispatchMessage(msg);
    23. }
    24. //回收正在使用的消息
    25. msg.recycleUnchecked();
    26. }
    27. }
    28. /**
    29. * 处理系统消息
    30. */
    31. public void dispatchMessage(@NonNull Message msg) {
    32. if (msg.callback != null) {
    33. handleCallback(msg);
    34. } else {
    35. if (mCallback != null) {
    36. if (mCallback.handleMessage(msg)) {
    37. return;
    38. }
    39. }
    40. //如果 Message.callback 为空,执行 handleMessage(msg),即回调复写 handleMessage(msg) 方法。
    41. handleMessage(msg);
    42. }
    43. }
    44. /**
    45. * 回收可能正在使用的消息,由 MessageQueue 和 Looper 在处理排队消息时在内部使用。
    46. */
    47. void recycleUnchecked() {
    48. //将消息标记为正在使用,同时将其保留在回收的对象池中。并清楚其余所有细节
    49. flags = FLAG_IN_USE;
    50. what = 0;
    51. arg1 = 0;
    52. arg2 = 0;
    53. obj = null;
    54. replyTo = null;
    55. sendingUid = UID_NONE;
    56. workSourceUid = UID_NONE;
    57. when = 0;
    58. target = null;
    59. callback = null;
    60. data = null;
    61. ···
    62. }
    63. 复制代码

    Looper 消息循环,发送消息给指定的 Handler中,触发我们覆写的 handleMessage(Message) 方法。

  • 相关阅读:
    MacOS 控制固态磁盘写入量,设置定时任务监控
    redis
    Node.js基础+原型链污染
    100条安全原则来制定安全策略
    std::integer_sequence 源码分析
    Mybatis中拦截器的使用场景和技巧
    掌握Python操作Word:从基础到高级全覆盖
    数据结构之顺序表
    非root用户,没有root权限,安装nginx
    2022.8.13
  • 原文地址:https://blog.csdn.net/xiaopangcame/article/details/127837278