Android Hanlder 揭密之路- 深入理解异步消息传递机制Looper、Handler、Message三者关系

在Android开发中,Handler作为实现线程间通信的桥梁,扮演着至关重要的角色。无论是在主线程执行UI操作,还是在子线程进行耗时任务,Handler都可以高效地将异步消息分派到对应的线程中执行。

本文将全方位解析Handler的工作原理及实现细节,从源码角度介绍Looper , Handler , Message的关系,让你记忆深刻。

一、Handler原理浅析

Handler实际是Android低层面向线程的消息循环机制MessageQueue的一层包装。

1、两个关键组成部分:

  • MessageQueue消息队列 - 用于存放所有通过Handler发送的消息
  • Looper消息循环器 - 负责不断从MessageQueue中取出消息,并按序执行

每个线程都可以通过Looper.prepare()方法创建自己的消息循环,并在循环体内通过Looper.loop()不断获取并执行消息。

Android 中的 Handler 机制是用于在不同线程之间进行通信和消息传递的重要机制。

2、工作流程如下

(1)、创建 Handler 实例,并重写 handleMessage() 方法,用于处理接收到的消息。

(2)、发送消息: 通过 HandlersendMessage() 等方法发送消息到消息队列。

(3)、消息循环: 消息队列会不断地从队列中取出消息,并分发给对应的 Handler 处理。

我们可以通过一个简单的示例来理解它的基本工作流程:

java 复制代码
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 创建 Handler 对象
        handler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                // 处理收到的消息
                switch (msg.what) {
                    case 0:
                        Log.d(TAG, "Received message: " + msg.obj);
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };

        // 在子线程中发送消息
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 创建消息对象
                Message message = Message.obtain();
                message.what = 0;
                message.obj = "Hello from worker thread!";

                // 发送消息
                handler.sendMessage(message);
            }
        }).start();
    }
}

在这个示例中,我们做了以下操作:

  • onCreate 方法中,我们创建了一个 Handler 对象,并重写了 handleMessage 方法。这个方法会在收到消息时被调用,我们在这里处理收到的消息。
  • 我们创建了一个子线程,在这个线程中创建了一个 Message 对象,并通过 handler.sendMessage() 方法将其发送给 Handler。
  • 当 Handler 收到消息时,它会将消息添加到消息队列中,然后等待 Looper 取出并处理这个消息。Looper 会调用我们重写的 handleMessage 方法来处理这个消息。

实际上,Handler 的实现机制要复杂得多,涉及到 MessageQueue、Looper 等多个组件的协作。但是理解了这个基本示例,就可以对 Handler 的工作原理有一个基本的认知了。

二、Handler 源码解析

下面我们来深入解析 Handler 的源码实现。

1、 Handler 使用回顾

我们先来回顾下Handler 使用流程

// 在主线程中创建 Handler 来处理子线程发送的消息
private Handler handler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
    super.handleMessage(msg);
    switch (msg.what) {
      case 0:
        //TODO: 处理消息
        break;
    }
  }
};

// 使用方式一:在子线程中发送消息
new Thread(new Runnable() {
  @Override
  public void run() {
    Message message = new Message();
    message.what = 0;
    message.obj = "测试消息";
    // 子线程中发送消息
    handler.sendMessage(message);
  }
}).start();

// 使用方式二:handler.post()
handler.post(new Runnable() {
  @Override
  public void run() {
    // 运行在子线程中...
  }
});

通过上面代码可以看到,在使用 Handler 时首先需要创建 Handler 对象。

接下来,我们看下 Handler 的构造方法。

2、Handler构造方法源码分析

//frameworks/base/core/java/android/os/Handler.java

/* 构造方法一 */
public Handler() {
  this(null, false);
}
/* 构造方法二 */
public Handler(Callback callback) {
  this(callback, false);
}
/* 构造方法三 */
public Handler(Looper looper) {
  this(looper, null, false);
}
/* 构造方法四 */
public Handler(Looper looper, Callback callback) {
  this(looper, callback, false);
}
/* 构造方法五 */
public Handler(boolean async) {
  this(null, async);
}
/* 构造方法六 */
public Handler(Callback callback, boolean async) {
  // ...
  mLooper = Looper.myLooper();
  if (mLooper == null) {
    throw new RuntimeException(
      "Can't create handler inside thread that has not called Looper.prepare()");
  }
  mQueue = mLooper.mQueue;
  mCallback = callback;
  mAsynchronous = async;
}
/* 构造方法七 */
public Handler(Looper looper, Callback callback, boolean async) {
  mLooper = looper;
  mQueue = looper.mQueue;
  mCallback = callback;
  mAsynchronous = async;
}

可以看到 Handler 有很多构造方法,常用的是构造方法一(实际也是调用的 :构造方法六)。

我们再来分析下构造方法六的源码:

//frameworks/base/core/java/android/os/Handler.java

/* 构造方法六 */
public Handler(Callback callback, boolean async) {
  // ...
  mLooper = Looper.myLooper();
  if (mLooper == null) {
    throw new RuntimeException(
      "Can't create handler inside thread that has not called Looper.prepare()");
  }
  mQueue = mLooper.mQueue;
  mCallback = callback;
  mAsynchronous = async;
}

可以看到,这里调用了 Looper.myLooper() 方法,当 mLooper 为空时会抛出异常,提示我们需要先调用 Looper.prepare() 方法,我接下来看下 Looper 中的这两个方法。

3、Looper源码分析

  • Looper 是 Handler 的核心组成部分之一。它是一个消息循环器,负责管理消息队列,并按照先进先出的顺序处理消息。
  • 每个线程都可以创建自己的 Looper,并且主线程(UI 线程)默认就会创建一个 Looper。
  • Looper 通过 Looper.prepare() 方法初始化,然后调用 Looper.loop() 方法开启消息循环。

(1)、Looper.java

//frameworks/base/core/java/android/os/Looper.java

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;

final MessageQueue mQueue;
final Thread mThread;

从上面源码中可以看到 Looper 有 4 个成员变量:

  • sThreadLocal:保存的是当前线程的 Looper。
  • sMainLooper:Application 中主线程中的 Looper。
  • mQueue:当前线程中的 MessageQueue。
  • mThread:创建 Looper 的线程。

(2)、myLooper

//frameworks/base/core/java/android/os/Looper.java

/* Handler 构造方法六中调用的方法 */
public static Looper myLooper() {
  // 返回当前线程中的 looper
  return sThreadLocal.get();
}

从上面的源码可见 myLooper() 逻辑很简单,调用了 ThreadLocal 的 get() 方法。ThreadLocal 我们稍后再分析。

(3)、prepare

在 Handler 构造方法六中可以看到,如果 myLoop() 的结果为空会直接抛出异常,提示需要先调用 prepare() 方法,接下来分析下 prepare() 方法。

/* Handler 构造方法六中调用的方法 */
public static void prepare() {
  prepare(true);
}
/* 带参数的 prepare 方法 */
private 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));
}
/* Looper 构造方法 */
private Looper(boolean quitAllowed) {
  mQueue = new MessageQueue(quitAllowed);
  mThread = Thread.currentThread();
}

prepare() 方法中调用了 prepare(quitAllowed) 方法,这里判断了 Looper 是否为空。

如果当前线程已经创建了 Looper 直接抛出异常,也就是说一个线程中只能创建一个 Looper,经常使用 Handler 的小伙伴应该对这个异常很熟悉。

如果当前线程没有创建 Looper 会直接调用 Looper(quitAllowed) 的构造方法,创建一个 Looper 并创建一个 MessageQueue,然后保存一下当前线程的信息。

4、MessageQueue源码分析

  • MessageQueue 是 Looper 的另一个核心组成部分,它是一个消息队列,负责存储和管理 Message 对象。
  • MessageQueue 提供了 enqueueMessage()next() 等方法来实现消息的入队和出队操作。

我们先看下 MessageQueue 的具体实现:

//frameworks/base/core/java/android/os/Looper.java

final MessageQueue mQueue;

/* Looper 构造方法 */
private Looper(boolean quitAllowed) {
  mQueue = new MessageQueue(quitAllowed);
  mThread = Thread.currentThread();
}

再看下MessageQueue 的构造方法:

//frameworks/base/core/java/android/os/MessageQueue.java

private native static long nativeInit();

MessageQueue(boolean quitAllowed) {
  mQuitAllowed = quitAllowed;
  mPtr = nativeInit();
}

MessageQueue 的构造方法逻辑比较简单。

这里调用了一个 native 方法 nativeInit() 在 native 层进行了初始化。

感兴趣的朋友可以去查看 native 源码,文件如下:

//frameworks/base/core/jni/android_os_MessageQueue.cpp

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
  NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
  if (!nativeMessageQueue) {
    jniThrowRuntimeException(env, "Unable to allocate native queue");
    return 0;
  }

  nativeMessageQueue->incStrong(env);
  return reinterpret_cast<jlong>(nativeMessageQueue);
}

至此, Handler 的创建流程已经分析完了,可以看到 Handler 创建流程如下图所示:

在创建 Handler 时:

第一步需要先调用 Looper.prepare(),该方法会初始化 Looper,创建 MessageQueue 和 ThreadLocal。

第二步会调用 Looper 中的 myLoop() 方法获取到 Looper 和 MessageQueue 保存到 Handler 中。

5、ThreadLocal 源码分析

如上,我们看见 第一步时,创建了 ThreadLocal 和 MessageQueue 。

(1)、ThreadLocal 到底是做什么的呢?

我们来分析下 ThreadLocal 的作用,源码如下:

ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
sThreadLocal.set(new Looper(quitAllowed)); // 设置变量信息
sThreadLocal.get(); // 读取变量信息

我们可以看到ThreadLocal在Android的消息循环机制中扮演了重要角色。

在Android中,每个线程都有自己的Looper对象,用于从MessageQueue中获取消息并执行。由于Looper对象是线程私有的,因此Android通过ThreadLocal来为每个线程维护自己的Looper实例。

具体来说:

  • ThreadLocal<Looper> sThreadLocal定义了一个线程本地存储对象,用于存放当前线程的Looper实例。

  • sThreadLocal.set(new Looper(quitAllowed))在当前线程中创建一个Looper对象,并通过set方法将其关联到当前线程。

  • sThreadLocal.get()则可以在当前线程中获取之前设置的Looper对象。

通过这种方式,Android就实现了每个线程拥有自己的Looper实例,可以独立地从MessageQueue获取和处理消息,避免了线程间数据混乱和竞争的问题。

ThreadLocal的这种线程隔离机制,使得Android的消息循环模型可以在多线程环境下高效、安全地运行,同时也体现了ThreadLocal在实现线程数据隔离方面的优秀作用。

(2)、ThreadLocal 的具体实现

接下来分析下 ThreadLocal 的具体实现。

//java/lang/ThreadLocal.java

private final int threadLocalHashCode = nextHashCode();

private static AtomicInteger nextHashCode =
    new AtomicInteger();

private static final int HASH_INCREMENT = 0x61c88647;

private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}

ThreadLocal 通过 threadLocalHashCode 来标识每一个 ThreadLocal 的唯一性。

threadLocalHashCode 通过 CAS 操作进行更新,每次 hash 操作的增量为 0x61c88647。

我们来看看 ThreadLocal 的 set() 方法。

//java/lang/ThreadLocal.java
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

可以看到通过 Thread.currentThread() 方法获取了当前的线程引用,并传给了 getMap(Thread) 方法获取一个 ThreadLocalMap 的实例。

getMap(Thread) 方法中直接返回 Thread 实例的成员变量 threadLocals。它的定义在 Thread 内部,访问级别为 package 级别:

//java/lang/Thread.java

ThreadLocal.ThreadLocalMap threadLocals = null;

到了这里,可以看出,每个 Thread 里面都有一个 ThreadLocal.ThreadLocalMap 成员变量,也就是说每个线程通过 ThreadLocal.ThreadLocalMapThreadLocal 相绑定,这样可以确保每个线程访问到的 ThreadLocal 变量都是本线程的。

获取了 ThreadLocalMap 实例以后,如果它不为空则调用 ThreadLocalMap.ThreadLocalMap.set() 方法设值;

若为空则调用 ThreadLocal.createMap() 方法 new 一个 ThreadLocalMap 实例并赋给 Thread.threadLocals。

(3)、ThreadLocalMap的源码分析

下面我们分析一下 ThreadLocalMap 的实现,可以看到 ThreadLocalMap 有一个常量和三个成员变量:

//java/lang/ThreadLocal.ThreadLocalMap

private static final int INITIAL_CAPACITY = 16;

private Entry[] table;

private int size = 0;

private int threshold; // Default to 0

其中 INITIAL_CAPACITY 代表这个 Map 的初始容量;table 是一个 Entry 类型的数组,用于存储数据;size 代表表中的存储数目; threshold 代表需要扩容时对应 size 的阈值。

Entry 类是 ThreadLocalMap 的静态内部类,用于存储数据。它的源码如下:

//java/lang/ThreadLocal.ThreadLocalMap

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

Entry 类继承了 WeakReference<ThreadLocal<?>>,即每个 Entry 对象都有一个 ThreadLocal 的弱引用(作为 key),这是为了防止内存泄露。一旦线程结束,key 变为一个不可达的对象,这个 Entry 就可以被 GC 回收了。

ThreadLocalMap 类有两个构造函数,其中常用的是 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue):

//java/lang/ThreadLocal.ThreadLocalMap

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

构造函数的第一个参数就是本 ThreadLocal 实例(this),第二个参数就是要保存的线程本地变量。构造函数首先创建一个长度为 16 的 Entry 数组,然后计算出 firstKey 对应的哈希值,然后存储到 table 中,并设置 size 和 threshold。

通过上面分析可以看到 ThreadLocal 的工作原理如下:

如图所示,ThreadLocal 中有一个 ThreadLocalMap 其中以 ThreadLocal 作为 Key,以需要保存的值作为 Value。这样不同的线程访问同一个 ThreadLocal 时,获取到的值也就是各个线程存储时对应的值了。

分析了 ThreadLocal ,接下来,我们再来看看MessageQueue 。

6、MessageQueue 源码分析

(1)、Handler.sendMessage()

我们常用的发消息的方法如下:

//frameworks/base/core/java/android/os/Handler.java

public final boolean sendMessage(Message msg) {
  return sendMessageDelayed(msg, 0);
}

public final boolean sendEmptyMessage(int what) {
  return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
  Message msg = Message.obtain();
  msg.what = what;
  return sendMessageDelayed(msg, delayMillis);
}

上面不管哪种发消息的方式,最后都调用了 sendMessageDelayed() 方法。

//frameworks/base/core/java/android/os/Handler.java

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
  if (delayMillis < 0) {
    delayMillis = 0;
  }
  return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean 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 false;
  }
  return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  msg.target = this;
  if (mAsynchronous) {
    msg.setAsynchronous(true);
  }
  return queue.enqueueMessage(msg, uptimeMillis);
}

sendMessageDelayed() 方法最后调用了 MessageQueue.enqueueMessage()

(2)、MessageQueue.enqueueMessage()

我们接着来看 enqueueMessage() 方法的实现:

//frameworks/base/core/java/android/os/MessageQueue.java

boolean enqueueMessage(Message msg, long when) {
  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.");
  }

  synchronized (this) {
    if (mQuitting) {
      IllegalStateException e = new IllegalStateException(
        msg.target + " sending message to a Handler on a dead thread");
      Log.w(TAG, e.getMessage(), e);
      msg.recycle();
      return false;
    }

    msg.markInUse();
    msg.when = when;
    Message p = mMessages;
    boolean needWake;
    if (p == null || when == 0 || when < p.when) {
      // 如果消息队列里面没有消息,或者消息的执行时间比里面的消息早,
      // 就把这条消息设置成第一条消息;
			// 一般不会出现这种情况,因为系统一定会有很多消息。
      msg.next = p;
      mMessages = msg;
      needWake = mBlocked;
    } else {
      // 如果消息队列里面有消息
      needWake = mBlocked && p.target == null && msg.isAsynchronous();
      Message prev;
      for (;;) { // 循环找到消息队列里面的最后一条消息
        prev = p;
        p = p.next;
        if (p == null || when < p.when) {
          break;
        }
        if (needWake && p.isAsynchronous()) {
          needWake = false;
        }
      }
      msg.next = p; // invariant: p == prev.next
      prev.next = msg; // 把消息添加到最后
    }

    if (needWake) {
      nativeWake(mPtr);
    }
  }
  return true;
}

分析到这里可以看到,我们通过调用 Handler.sendMessage() 最后将 Message 添加到了 MessageQueue 的消息队列中。

在前面 Looper.loop() 方法中分析过,loop() 方法中有一个死循环一直在读取消息,当读取到刚才添加的消息后会回调到 Handler.dispatchMessage() 方法。

到这里, Handler 的工作流程大家应该已经很清楚了。

如下图所示,假设在 Thread 1 中创建了 Handler,那么 Thread 2 向 Thread 1 发送消息的过程。

Handler 机制就像是一个传送机器,Looper 就是传送轮一直在不停的旋转,MessageQueue 就是传送带跟着Looper 旋转来运输 Message,Handler 就是机械手在 Thread 2 中将 Message 放到传送带 MessageQueue 上,传送到 Thread 1 后再将 Message 拿下来通知 Thread 1 进行处理。

(3)、Handler.post() 源码

了解了 Handler 工作流程,我们继续来分析下另一种使用方式 Handler.post()

//frameworks/base/core/java/android/os/Handler.java

public final boolean post(Runnable r) {
  return  sendMessageDelayed(getPostMessage(r), 0);
}

可以看到 post() 也是调用了 sendMessageDelayed() 方法。

(4)、getPostMessage 源码

我们再来看下 getPostMessage(r) 方法的实现。

//frameworks/base/core/java/android/os/Handler.java

private static Message getPostMessage(Runnable r) {
  Message m = Message.obtain();
  m.callback = r;
  return m;
}

原来这里创建了一个 Message,将 Runnable 放入了 Message 的 callback 上。

(5)、dispatchMessage 源码

那 Message 最后怎么处理的呢?

Looper.loop() 方法中有这么一句 msg.target.dispatchMessage(msg)

//frameworks/base/core/java/android/os/Handler.java
public void dispatchMessage(Message msg) {
  if (msg.callback != null) {
    handleCallback(msg); // 处理 post 消息,稍后再分析
  } else {
    if (mCallback != null) {
      // 回调到 Handler.handleMessage() 方法
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    handleMessage(msg);
  }
}

handleCallback() 就是处理 Handler.post() 发送的消息:

//frameworks/base/core/java/android/os/Handler.java

private static void handleCallback(Message message) {
  message.callback.run();
}

如此简单,就是拿到 Runnable 调用了 run() 方法。

至此, 关于Handler,Looper ,Message 这三者关系上面已经叙述的非常清楚了。

让我们首先总结一下:

  1. Looper.prepare()方法会为当前线程创建一个Looper实例,其内部含有与之关联的MessageQueue对象。同一线程内只能调用一次prepare(),因此MessageQueue在线程内是单例的。

  2. Looper.loop()会让当前线程进入无限循环模式,不断从MessageQueue中读取消息,并通过msg.target.dispatchMessage(msg)将消息分发给相应的Handler进行处理。

  3. 在构造Handler实例时,会获取当前线程的Looper,并将Handler的MessageQueue与Looper内部的MessageQueue建立关联。

  4. 调用Handler的sendMessage()方法时,会给Message设置target为当前Handler实例,并将Message加入关联的MessageQueue中等待分发。

  5. 在Handler的handleMessage()方法中,我们可以重写自定义的消息处理逻辑,这个方法最终会由Looper.loop()中的msg.target.dispatchMessage(msg)回调执行。

我们再来张图一目了然 :

五、结语

伴随着Android版本不断迭代,Handler机制也在持续优化和完善,Android 12中引入了高效模式MessageQueue和SyncQueueRemover工具等新特性。离开Handler,消息驱动架构也延伸出各种优秀的开源替代品,比如EventBus、RxJava。

未来Handler如何发展,我们拭目以待,不过这些基础底层原理的理解对开发者而言永远都是重中之重,期待您在实践中有更多思考和总结。

相关推荐
拭心9 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王12 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡12 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道12 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库13 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道14 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe14 小时前
Android Hook - 动态加载so库
android
居居飒15 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He18 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗18 小时前
Android笔试面试题AI答之Android基础(1)
android