在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)、发送消息: 通过 Handler
的 sendMessage()
等方法发送消息到消息队列。
(3)、消息循环: 消息队列会不断地从队列中取出消息,并分发给对应的 Handler
处理。
我们可以通过一个简单的示例来理解它的基本工作流程:
public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private Handler handler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 创建 Handler 对象handler = new Handler(Looper.getMainLooper()) {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 0://TODO: 处理消息break;}}
};// 使用方式一:在子线程中发送消息
new Thread(new Runnable() {@Overridepublic void run() {Message message = new Message();message.what = 0;message.obj = "测试消息";// 子线程中发送消息handler.sendMessage(message);}
}).start();// 使用方式二:handler.post()
handler.post(new Runnable() {@Overridepublic 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.javastatic 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() {// 返回当前线程中的 looperreturn 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.javafinal MessageQueue mQueue;/* Looper 构造方法 */
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();
}
再看下MessageQueue 的构造方法:
//frameworks/base/core/java/android/os/MessageQueue.javaprivate native static long nativeInit();MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();
}
MessageQueue 的构造方法逻辑比较简单。
这里调用了一个 native 方法 nativeInit()
在 native 层进行了初始化。
感兴趣的朋友可以去查看 native 源码,文件如下:
//frameworks/base/core/jni/android_os_MessageQueue.cppstatic 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.javaprivate 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);elsecreateMap(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.javaThreadLocal.ThreadLocalMap threadLocals = null;
到了这里,可以看出,每个 Thread 里面都有一个 ThreadLocal.ThreadLocalMap
成员变量,也就是说每个线程通过 ThreadLocal.ThreadLocalMap
与 ThreadLocal
相绑定,这样可以确保每个线程访问到的 ThreadLocal 变量都是本线程的。
获取了 ThreadLocalMap 实例以后,如果它不为空则调用 ThreadLocalMap.ThreadLocalMap.set() 方法设值;
若为空则调用 ThreadLocal.createMap() 方法 new 一个 ThreadLocalMap 实例并赋给 Thread.threadLocals。
(3)、ThreadLocalMap的源码分析
下面我们分析一下 ThreadLocalMap 的实现,可以看到 ThreadLocalMap 有一个常量和三个成员变量:
//java/lang/ThreadLocal.ThreadLocalMapprivate 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.ThreadLocalMapstatic 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.ThreadLocalMapThreadLocalMap(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.javapublic 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.javapublic 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.javaboolean 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.nextprev.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.javapublic final boolean post(Runnable r) {return sendMessageDelayed(getPostMessage(r), 0);
}
可以看到 post()
也是调用了 sendMessageDelayed()
方法。
(4)、getPostMessage 源码
我们再来看下 getPostMessage(r)
方法的实现。
//frameworks/base/core/java/android/os/Handler.javaprivate 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.javaprivate static void handleCallback(Message message) {message.callback.run();
}
如此简单,就是拿到 Runnable 调用了 run()
方法。
至此, 关于Handler,Looper ,Message 这三者关系上面已经叙述的非常清楚了。
让我们首先总结一下:
-
Looper.prepare()方法会为当前线程创建一个Looper实例,其内部含有与之关联的MessageQueue对象。同一线程内只能调用一次prepare(),因此MessageQueue在线程内是单例的。
-
Looper.loop()会让当前线程进入无限循环模式,不断从MessageQueue中读取消息,并通过msg.target.dispatchMessage(msg)将消息分发给相应的Handler进行处理。
-
在构造Handler实例时,会获取当前线程的Looper,并将Handler的MessageQueue与Looper内部的MessageQueue建立关联。
-
调用Handler的sendMessage()方法时,会给Message设置target为当前Handler实例,并将Message加入关联的MessageQueue中等待分发。
-
在Handler的handleMessage()方法中,我们可以重写自定义的消息处理逻辑,这个方法最终会由Looper.loop()中的msg.target.dispatchMessage(msg)回调执行。
我们再来张图一目了然 :
五、结语
伴随着Android版本不断迭代,Handler机制也在持续优化和完善,Android 12中引入了高效模式MessageQueue和SyncQueueRemover工具等新特性。离开Handler,消息驱动架构也延伸出各种优秀的开源替代品,比如EventBus、RxJava。
未来Handler如何发展,我们拭目以待,不过这些基础底层原理的理解对开发者而言永远都是重中之重,期待您在实践中有更多思考和总结。