一、写在前面
在Android中Handler主要被用于线程间切换,比如在子线程中访问网络数据之后需要将数据更新到View中则需要使用到Handler进行数据处理。
1、涉及到的类
本篇文章会对如下几个类所涉及到的关键函数进行解析,以对Handler底层实现原理进行学习。
- ActivityThread.java:当前应用进程启动的时候会调用到该类的main函数,在该函数中会创建UI线程的Looper对象并调用其loop函数以启动无限循环防止进程退出;
- Looper.java:死循环调用MessageQueue中的next函数;
- MessageQueue.java:Message入队列、出队列处理类,Looper中无限循环调用被阻塞就发生在该类的next函数中;
- Handler.java:本篇文章主角类。
2、Handler简单使用
如下以在UI线程中创建Handler为例,对Handler的使用进行简单的学习。
2.1 Handler定义
static class MyHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
......
//业务逻辑,当前逻辑处理则发生在UI线程中
}
}
2.2 Handler使用
//以UI线程Looper对象创建Handler,如果在子线程使用Handler则可以先创建HandlerThread对象然后通过HandlerThread对象获取Looper对象
Handler handler = new MyHandler();
Message message = handler.obtainMessage();
message.what = 1;
message.obj = new Object();
//该函数调用则可以发生在子线程中
handler.sendMessage(message);
二、源码
如下以ActivityThread->Looper->MessageQueue->Handler的顺序对其中所涉及到的关键源码进行解析,当然有需求的也可以逆序看。
1、ActivityThread.main
该函数作为当前应用进程的入口函数(参考Android应用进程启动源码),其首先通过调用Looper.prepareMainLooper
函数为当前进程的UI线程创建Looper对象,最后通过调用Looper.loop
函数以防止进程无任务处理而退出,同时等待Handler发送Message进行处理。部分源码如下:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
......
//为当前进程UI线程创建Looper对象
Looper.prepareMainLooper();
......
//防止进程死亡,同时等待Handler发送Message进行处理
Looper.loop();
//loop意外退出,一般情况下不会出现
throw new RuntimeException("Main thread loop unexpectedly exited");
}
2、Looper
2.1 preparexx
通过调用如下函数中的其中一个则可以为当前线程成功创建Looper对象,并将创建好的Looper对象保存到ThreadLocal对象中。
ThreadLocal:线程内部数据存储类。通过该类可以在指定的线程中存储数据,并只能在存储了对应数据的线程中才能正确获取到对应数据,其他线程则无法获取到该数据。
public static void prepareMainLooper() {
//调用prepare函数创建Looper对象并保存到ThreadLocal对象中
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//从ThreadLocal对象获取Looper对象
sMainLooper = myLooper();
}
}
public static void prepare() {
prepare(true);
}
//quitAllowed:表示是否允许退出当前Looper,对于UI线程中的Looper而言则不允许退出
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建Looper对象并保存到ThreadLocal对象中
sThreadLocal.set(new Looper(quitAllowed));
}
2.2 构造函数
private Looper(boolean quitAllowed) {
//创建MessageQueue对象给Handler使用
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
2.3 myLooper
//获取当前线程对应的Looper对象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
2.4 loop
当前线程(比如UI线程)通过调用该函数以防止进程退出。代码比较简单,核心就是通过for启动无限循环并调用函数loopOnce。
public static void loop() {
//获取当前线程Looper对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
......
me.mSlowDeliveryDetected = false;
//死循环不断调用loopOnce函数
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
2.5 loopOnce
如下代码省略掉了大部分不相关的函数实现。核心逻辑就是调用到函数MessageQueue.next
(该函数会阻塞当前线程)以获取下一个能够处理的Meesage,并最终调用到Handler中进行业务逻辑处理。注意该处调用是发生在创建Looper对象的线程中(比如UI线程),这也是能够实现线程切换的重点。
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next();
//如果没有Message需要处理并且没有被阻塞,则说明上层业务方主动调用了quit函数并且当前looper能够退出
//则结束当前Looper
if (msg == null) {
return false;
}
......
try {
//target对应Message的Handler对象,最终会调用到handleMessage函数
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
return true;
}
3、MessageQueue
3.1 next
如下代码做了如下几件事情:(1)判断当前MessageQueue是否为空,如果为空则设置下次等待时间为-1,即永久等待直到被唤醒为止;(2)如果不为空则判断队列首部Message是否到达了处理时间,如果到了则立马返回进行处理,否则计算等待时间间隔,重新等待。
Message next() {
final long ptr = mPtr;
//判断是否初始化成功
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
//阻塞时间,这里有三个值
//(1)0:不阻塞
//(2)-1:阻塞,直到被唤醒为止
//(3)>0:阻塞特定时间之后自动唤醒
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//首次进来则不阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//获取当前Message队列
Message msg = mMessages;
//一般情况下msg.target不会为空,因此这里暂时不看
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//判断当前时间是否小于队列首部Message时间,如果小于则说明不存在Message需要处理
//注意Message队列首部时间最小,越往后时间越晚
if (now < msg.when) {
//获取最近需要处理Message时间距离当前时间间隔,以计算下次需要等待时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
//存在Message到达处理时间
} else {
mBlocked = false;
//目前分析情况下prevMsg为Null
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
//移除即将被处理的队列头部Message
mMessages = msg.next;
}
msg.next = null;
//设置当前Message正在被处理flag
msg.markInUse();
//返回队列首部Message进行处理
return msg;
}
//Message队列为空,因此将等待时间设置为-1(即永久等待直到被唤醒为止)
} else {
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//说明需要其他地方来进行唤醒,比如首个Message进入队列的时候
mBlocked = true;
continue;
}
......
nextPollTimeoutMillis = 0;
}
}
3.3 enqueueMessage
如下代码主要就是按照时间先后顺序将当前Message插入到MessageQueue的指定节点处。MessageQueue中的节点Message对应处理时间是头部->尾部依次递增的。
boolean enqueueMessage(Message msg, long when) {
//不允许Message对应Handler对象为null
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
//当前Message正在被处理或者正在入队列,在上述next函数中有相关标记代码
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//当前Looper正在退出
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;
}
//表示当前Message正在入队列
msg.markInUse();
//Message处理时间,用于后续入队列以及设置等待时间
msg.when = when;
//当前Message队列
Message p = mMessages;
//是否需要主动唤醒next函数中的阻塞
boolean needWake;
//说明当前Message是第一条Message,因此需要主动唤醒
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//p.target一般不会为null,因此这里为false
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;
prev.next = msg;
}
//判断是否主动唤醒next函数中的阻塞,第一次进来一般都需要主动唤醒
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
4、Handler
4.1 enqueueMessage
Handler中有多个函数都可以将Message插入到MessageQueue中,不过最终都会调用到enqueueMessage函数中。代码如下:
/**
* queue:从当前线程Looper对象中获取
* msg:需要入队列的Message对象
* uptimeMillis:Message处理时间,SystemClock.uptimeMillis() + 延迟时间,或者业务方传递
*/
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//当前Handler对象
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
//默认为false
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//将Message加入到MessageQueue中
return queue.enqueueMessage(msg, uptimeMillis);
}
4.2 dispatchMessage
如下代码就是Looper.loopOnce
最终调用到的代码了。其首先会判断处理的Message对象和当前Handler对象是否存在CallBack,如果存在则优先调用CallBack对象,否则调用到当前Handler的handleMessage函数。
public void dispatchMessage(@NonNull Message msg) {
//判断Message是否设置了CallBack,通过调用Handler中的postxx函数进行设置。
if (msg.callback != null) {
handleCallback(msg);
} else {
//判断当前Handler是否设置了CallBack(可通过Handler构造函数传入,hook ActivityThread中Handler对象重点)
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//如果Message和Handler都没有设置CallBack则调用到handleMessage,后续就走到了我们的业务逻辑。
handleMessage(msg);
}
}
三、总结
如上即为Handler底层大致源码了,其能够实现线程间切换的主要原理就是创建Looper对象的线程(比如UI线程)会不断循环(如果没有Message或者Message处理时间未到则会被阻塞)读取MessageQueue中是否有需要处理的Message,如果有则将需要处理的Message发送给Handler进行处理;而其他子线程则只需要通过对应的Handler对象将需要处理的Message插入到MessageQueue即可。
总之一句话,Handler中处理的业务逻辑最终运行在哪个线程中,取决于创建Handler时传递进去的Looper对象在哪个线程中被创建并调用其loop函数。