吐槽:想起大约一周前的一个夜晚,我正在好好地学习。忽然之间。。。胸口一阵疼痛,而后就是一直疼,而且随着深呼吸会更加重痛。我。。。。作错了什么???难道嫉妒我这么爱学习就要猝死了???怎么能这样!!!那天晚上,我忍着痛入眠,次日醒来,疼痛依然伴随吾身。。。去医院。。。一顿拍片检查操做以后,医生:“嗯,你肺里进空气了。”我(满脸问号):“???”,我肺里没空气我怎么活的?医生(一脸平静):“肺泡破了吧,空气进入胸腔了,压缩了肺部”。。。。好吧,是气胸。得,想我一不运动,二不高又不瘦,我会得这种病也是**奇了怪了。而后就是。。天天吸氧、拍片复查。。重复重复。。。直到如今,我仍是不能理解,,,为何我会得这种病???java
零零碎碎的东西老是记不长久,仅仅学习别人的文章也只是他人咀嚼后留下的残渣。无心中发现了这个每日一道面试题 ,想了想若是只是简单地去思考,那么不只会收效甚微,甚至难一点的题目本身可能都懒得去想,坚持不下来。因此不如把每一次的思考、理解以及别人的看法记录下来。不只加深本身的理解,更要激励本身坚持下去。git
Handler 想必接触 Android 的都已经很熟悉了,咱们一般用用于子线程与主线程的通讯,并在主线程中处理相关消息,好比更改 UI 等。Handler 的消息机制为咱们处理线程中的通讯确实方便了许多。github
最简单的使用就是在主线程中建立 Handler 对象并重写 handlerMessage() 方法处理消息,在子线程中经过 handler 对象的 sendMessage 方法发送消息。面试
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// TODO write your message processing code
...
}
};
new Thread(){
@Override
public void run() {
super.run();
//TODO handling events
...
handler.sendMessage(Message.obtain());
}
};
复制代码
为了简单示例,Handler 才这样写的。这种建立方法会形成内存泄漏,具体愿意以及解决方案在每日一道面试题(第 1 期)---自定义 handler 如何有效保证内存泄漏问题 都已经说明,这里再也不赘述。app
而关于 Handler 的原理,咱们可能会多多少少的了解到与 Looper、Message、MessageQueue 都是离不开的,那么具体是怎麽配合呢?下面我根据消息机制的过程,结合源码一步一步的解析。异步
在咱们使用时,首先就是建立 Handler,那咱们看一下 Handler 的构造函数都干了些什么吧。async
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//获取本地 TLS 存储区的 Looper 对象引用
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//使用 Looper 中的 MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
复制代码
好吧,不少构造函数,不过有用的就是这两个,其余都是调用这两个而已。从两个对比能够看出,若是你没有传入 Looper 对象,那么就会经过 Looper.myLooper() 获取。传入的话,就是用自定义的,而且 MessageQueue 一直是使用 Looper 中的 MessageQueue,因此这里出现了第两个重要结论:ide
而后还有两个参数 callBack 和 async,callBack 是 Handler 内部的一个接口,内部只有一个 handleMessage() 方法,做用咱们下面讲到再说。而后就是 async,这个就好理解了,就是决定是异步处理消息,仍是同步处理消息,默认为同步 false;函数
public interface Callback {
public boolean handleMessage(Message msg);
}
复制代码
上一个步骤中咱们知道,Handler 是直接从 Looper.myLooper() 中得到对象的,咱们具体探讨一下。oop
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
复制代码
很简单,是从一个集合中拿到的,这个 sThreadLocal,是 ThreadLocal 类的对象,表明着本地存储区(Thread Local Storage 简称 TLS),线程中的惟一 Looper 对象就存储在这里,每一个线程都有一个本地存储区域,而且是私有的,线程之间不能相互访问。咱们寻找一下是在哪一个地方插入的
public static void prepare() {
prepare(true);
}
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));
}
复制代码
找到啦,由两个方法的权限访问修饰符可知,咱们只能调用第一个,也就是说,第二个方法的参数永远是 true。再来看第二个方法,首先就是一个异常捕获,有的人可能已经很熟悉了,那就是一个线程中只能有一个 Looper。后面就是没有的话就 new 一个 Looper 对象,构造函数中都干了什么呢?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
复制代码
也很简单,初始化了 mQueue 也就是 MessageQueue,还有就是当前所在的线程 mThread。从权限访问修饰符能够看出,这是一个私有的构造方法,因此说,咱们建立 Looper 方法只有 prepare() 方法啦。
上述 Looper 对象的建立中,new 了一个 MessageQueue 方法,咱们看看都干了什么。
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
复制代码
也很简单,初始化了两个变量 mQuitAllowed 与 mPtr,第一个表明着消息队列是否能够退出,上面也说了,咱们么的办法,只能是 true。第二个 mPtr,涉及到 Native 层的代码,在 native 层也作了一个初始化,具体深刻了解可到此处Android 消息机制 2-Handler(Native 层)
建立完 Handler,下一步就是发送 Message 了,去看看源码
public final boolean sendMessage(Message msg){};
public final boolean sendEmptyMessage(int what){};
public final boolean sendEmptyMessageDelayed(int what, long delayMillis){};
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis){};
public final boolean sendMessageDelayed(Message msg, long 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);
}
复制代码
又是好多发送消息的,不过他们最后都调用了 sendMessageAtTime 方法。第一个参数是要发送的消息,而第二个是一个绝对时间,也就是发送消息的时间。在作了一些判断以后,调用了 enqueueMessage 方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//消息得到发送该消息的 Handler 对象引用
msg.target = this;
//是否同步属性
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
在又作了一些 Message 的属性初始化后,调用了 queue 的 enqueueMessage 方法,而这个 queue 就是在 Looper 对象中得到的 MessageQueue 对象。下面是 MessageQueue 中的 enqueueMessage 方法
boolean enqueueMessage(Message msg, long when) {
// 每个 Message 必须有一个 target
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) { //正在退出时,回收 msg,加入到消息池
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;//mMessages 为当前消息队列的头结点
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//p 为 null(表明 MessageQueue 没有消息) 或者 msg 的触发时间是队列中最先的, 则进入该该分支
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//将消息按时间顺序插入到 MessageQueue。通常地,不须要唤醒事件队列,除非
//消息队头存在 barrier,而且同时 Message 是队列中最先的异步消息。
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;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
复制代码
总结就是,MessageQueue 按照消息的触发时间插入队列,队头是最先要触发的消息。一个新的消息加入会根据触发时长从队头开始遍历。
好了,消息已经发送给 MessageQueue 了,那么谁来管理这个 MessageQueue,将其中的消息正确的、准确的分发呢?那就是 Looper,准确的说是 Looper 中的 loop() 方法,这也是咱们在子线程中建立 Handler 先要以前要 Looper.perpare(),以后要 Looper.loop() 的缘由。
public static void loop() {
final Looper me = myLooper();//获取 TSL 存储区的 Looper 对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//获取相对应的消息队列
................
for (;;) {//消息主循环,除非线程退出,否则会一直循环,没有消息时会阻塞
//获取下一个消息,没有消息时会阻塞,消息队列退出后会返回 null,则该循环也退出
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
// 默认为 null,可经过 setMessageLogging() 方法来指定输出,用于 debug 功能
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
................
try {
//获取消息后根据 msg 的 target 即所属 Hnadler 分发消息
//target 即 Handler 在上面发送消息代码解释中有说明
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
.................
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
................
//分发完此消息,就回收此 Message 对象,留以复用
msg.recycleUnchecked();
}
}
复制代码
一些代码解释已经很清楚了,在在这里面主要是一个死循环轮询消息。由 MessageQueue 的 next() 方法取出消息,再由 Message 所属的 Handler 对象 dispatchMessage() 方法分发消息。首先咱们看 next() 方法。
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {//当消息循环已经退出,则直接返回
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;//阻塞时长
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞函数,参数 nextPollTimeoutMillis 表示等待时长,或者消息队列被唤醒,都会返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//当消息中的 Handler 为空时,在 MessageQueue 中寻找下一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
//下一个消息尚未到时间发送,设置阻塞时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 得到消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
//改变 message 状态
msg.markInUse();
return msg;
}
} else {
// No more messages.
//没有消息,设置阻塞时长
nextPollTimeoutMillis = -1;
}
// 若是正在退出,返回空
if (mQuitting) {
dispose();
return null;
}
.............
}
}
复制代码
nativePollOnce 是一个阻塞函数,参数 nextPollTimeoutMillis 则表明阻塞时长,当值为-1 时,则会一直阻塞下去。 因此说,next 函数在 MessageQ 有消息时,会获取消息并返回,在没有消息时,则会一直阻塞。
获取完消息,就要到分发消息了,也就是消息轮询总结中的 dispatchMessage 函数,在 Handler 类中
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
这里的调用流程就是
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
复制代码
public interface Callback {
public boolean handleMessage(Message msg);
}
复制代码
public void handleMessage(Message msg) {
}
复制代码
转了一圈,从 Handler 到 Looper 又到 MessageQ 最后又回到 Handler,整个消息机制也就差很少这样啦,固然 Message 在其中就是一个实体啦,能够协上数据一块儿传递消息。我也是根据这个顺序,一步一步的慢慢了解每一个步骤的。
最后,附上一张图吧,会更清晰一点