仍然记得刚开始学习android时,一直使用handler发送消息更新UI,只知道handler可以把消息发送到主线程。在后来随着工作经验的积累,发现Hanldler在整个Android系统中的角色举足轻重,它的产生并不只是用于UI处理,而更多的是handler是整个app的通信框架,我们可以在ActivityThread里面感受到,整个app都是用它来进行线程间的协调。
往事回忆
时至今天我仍然记得,刚出来那会去面试,面试官问:你了解handler吗?那我肯定回答:了解啊。接下来他问:在子线程中创建handler要注意什么问题?这我就有点蒙了,我是真不知道,因为从没在子线程中创建过handler,包括在后来的工作中,我从没在子线程中创建handler。当时在回去的路上,我觉得这面试官有点装,因为跟我扯了很多系统源码,恰恰我根本不懂,后来随着工作经验的积累,慢慢明白,当初别人面试我的用意,因为只有在了解了源码的工作原理后,你才能在面对疑难杂症,如何寻找解决问题的突破点。
handler工作流程
image.png源码分析
1,sendMessage(msg:Message)
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
//当前时间加上延迟时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
//mQueue 在 Looper构造方法中创建 并绑定
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);
}
跟踪sendMessage代码,发现最后都调用了sendMessageAtTime()方法,其实源码都为我们默认加了延迟时间,当我们没加延迟时间时系统默认为我们添加了0,就是不延迟。
2,enqueueMessage 消息队列
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
//mAsynchronous 为false new Handler()时源码为false
// public Handler() {
// this(null, false);
// }
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
boolean enqueueMessage(Message msg, long when) {
//判断msg是否与Handler绑定
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//判断msg是不是在使用
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;
//判断消息队列里是否有消息,
//如果是第一次添加消息,或者当前msg的时间小于mMessage的时间
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
//把当前消息添加到链表中的第一个,若此时消息队列处于等待状态则唤醒
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
//不是第一次添加数据,并且msg的时间大于 mMessage(头指针)的时间
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
//调用本地方法
nativeWake(mPtr);
}
}
return true;
}
跟踪代码到最后发现调用了native方法,无法再继续跟踪,根据方法字面意思大概是唤醒某个机制,上面这些代码始终没有handlerMessage,但是我们发现系统将我们的消息都插入到了一个链表MessageQueue中,在前面我们提到handler,MessageQueue,以及Looper的关系,所以我们联想到Looper一定在从MessageQueue轮询取消息。
3,Looper消息轮询
Looper的创建
在前面提到的面试过程中,在子线程中如何创建handler问题,将在此处详细解释,
Thread{
val handler = Handler()
handler.sendMessage(handler.obtainMessage())
}.start()
当我们在使用上述这段代码处理问题时,结果程序挂了
image.png
报错信息提示无法创建hander,在我们没有调用Looper.prepare()的时候,所以解决这个问题就是我们要手动调用Looper.prepare()方法。
Thread {
Looper.prepare()
val handler = Handler()
handler.sendMessage(handler.obtainMessage())
Looper.loop()
}.start()
其实在上面的面试中,当我们回答玩这个问题的时候,面试官一定会接着往下问:为什么我们平时在主线程中使用handler时,并没有调用Looper.prepare(),却可以使用handler?其实在主线程中,不是不会报错,只是系统为我们调用了,进入ActivityThread中找到程序的入口函数:
public static void main(String[] args) {
// ... 省略部分代码
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
从上面代码中我们清楚的知道自己创建的时候调用了Looper.prepare(),ActivityThread创建的时候调用了prepareMainLooper(),如果我们弄清了这两个方法到底做了什么,应该问题就差不多了,我们先看ActivityThread中调用的prepareMainLooper()方法:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
sThreadLocal.set(new Looper(quitAllowed));主要是为了保证一个线程只有一个Looper对象,其它代码都是相对简单的,Looper.loop()做了什么:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
//一个死循环 不停轮询消息
for (;;) {
//不断的从消息队列里面取消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 通过 target 去 dispatchMessage 而 target 就是绑定的 Handler
//创建enqueueMessage时绑定,前面代码有注释
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);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//消息回收循环利用,所以在我们发消息的时候尽量使用obtain()获取message对象
//不要new Message()这样循环利用消息,就不会导致浪费资源
msg.recycleUnchecked();
}
}
看到这里我们基本理清了Handler整个消息发送,存储,取出,处理过程,在整个Handler源码中,这些只是很浅显的一部分,比如它是如何线程同步的,消息同步屏障这些难点问题包括前面提到的obtain()消息,这里面就使用了享元模式的设计思想,包括整个handler消息处理其实是成产与消费的过程,,,等等,这些都值得我们一一去阅读源码分析,源码中有太多宝藏值得我们挖取,如果你觉得分析的不到位, 欢迎留言讨论。
网友评论