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

相关推荐
inmK12 小时前
蓝奏云官方版不好用?蓝云最后一版实测:轻量化 + 不限速(避更新坑) 蓝云、蓝奏云第三方安卓版、蓝云最后一版、蓝奏云无广告管理工具、安卓网盘轻量化 APP
android·工具·网盘工具
giaoho2 小时前
Android 热点开发的相关api总结
android
咖啡の猫3 小时前
Android开发-常用布局
android·gitee
程序员老刘4 小时前
Google突然“变脸“,2026年要给全球开发者上“紧箍咒“?
android·flutter·客户端
Tans54 小时前
Androidx Lifecycle 源码阅读笔记
android·android jetpack·源码阅读
雨白4 小时前
实现双向滑动的 ScalableImageView(下)
android
峥嵘life4 小时前
Android Studio新版本编译release版本apk实现
android·ide·android studio
studyForMokey7 小时前
【Android 消息机制】Handler
android
敲代码的鱼哇7 小时前
跳转原生系统设置插件 支持安卓/iOS/鸿蒙UTS组件
android·ios·harmonyos
翻滚丷大头鱼7 小时前
android View详解—动画
android