但愿你看完这篇文章后也能够把流程本身讲出来,而且每一个环节还能够讲出不少细节java
他的消息机制离不开Looper、MessageQueueweb
Looper
每一个线程只能持有一个,主要负责循环查看 MessageQueue
里面是否有 msg
须要处理,并将须要处理的消息取出,交给 Handler
MessageQueue
是负责存放消息的,数据结构是一个单链表,这样就能够方便地插入或删除 msg
具体流程通常是:api
Handler
发送一条msg
=> 本质是向MessageQueue
里插入一条msg
,插入时候的依据是msg.when
=> SystemClock.uptimeMillis() + delayMillis
markdown
这条msg
被MessageQueue.next()
返回并交给Handler
去处理数据结构
next()
会在有同步屏障(msg.target==null
)的时候遍历查找并返回最先的异步消息,并在移除屏障后,从头取出并返回消息异步
Handler.dispatchMessage(msg)
会优先处理msg.callback
,若是msg.callback
为空,就处理Handler.mCallback
,而后处理是msg
自己 msg.callback
是在调用Handler.post(Runnable)
时,里面的Runnable
(runOnUIThread
,view.post(Runnable)
也用的是Handler.post(Runnable)
,Runnable
是同样的)async
这是在不新增Handler
的状况下,另外一种调用Handler
的方式(以下)ide
class MyHandlerCallBack: Handler.Callback {
override fun handleMessage(msg: Message?): Boolean {
TODO("Not yet implemented")
}
}
复制代码
能够看到他也有handleMessage
这个方法oop
目的就是让主线程一直卡在这个死循环里面post
由于Looper的做用就是在这个死循环里面取出消息,而后交给Handler
处理
Android的生命周期,你了解的onCreate,onStop,onStart...... 等等都是由Handler
来处理的,都是在这个死循环里面运行的
因此什么Looper
死循环卡死主线程怎么办???
必须给我卡住!!!不卡住的话,消息就无法整了!!!
看下Android启动的时候的源码 Activitythread.java >> main()
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
复制代码
想一想写java的时候,main最后一行执行完了,不就完全玩完了嘛!!!
其实想都不用想,一直在看MessageQueue
里面有没有消息呗,太简单了!
咋看?
答: 调用MessageQueue.next()
看下源码 Looper.java >> loop()
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
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);
}
}
...
}
复制代码
很简单,next()
返回Message
,msg.target.dispatchMessage()
处理Message
可是队列里没消息就会返回null,这是错误的!!!具体往下看
Handler
发消息的时候,目的就是对msg
通过一系列操做,最终也只是调用enqueueMessage
插入队列而已
看下源码 Handler>>enqueueMessage()
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
return
直接调用Message的插入队列方法
出队就是next()
方法,以前已经见过了
Message
是按时间排序的,也就是msg.when
=> SystemClock.uptimeMillis() + delayMillis
msg.when
是Message
指望被处理的时间
SystemClock.uptimeMillis()
是开机到如今的时间,delayMills
是延迟时间,这个在sendMessageDelayed
方法里直接能够直接传参
next()
就是按照时间顺序处理MessageQueue
里面的消息的
可是next()
里有个概念叫 同步屏障
同步屏障,就是说,平时MessageQueue
都是处理同步消息,也就是按顺序来,一个个出队
同步屏障就是阻挡同步消息的意思
就是msg.target == null
的时候,MessageQueue
就会去找msg.isAsynchronous()
返回true
的msg
isAsynchronous,没错 ! 这是异步消息,就是优先级很高,须要马上执行的消息,好比:更新View
值得注意的是,讲Looper的时候,源码next()
后面官方给咱们注释了 // might block
可能阻塞,也就是说可能这个next()
也许会执行很久
next()
会阻塞?,何时阻塞?
now < msg.when
也就是时间还没到,指望时间大于如今的时间
另外看第一行,只有ptr == 0
,才会返回null
因此上面才说next()
不会由于没消息而返回null
,原来返回null
的时候在这呢!
看下源码,MessageQueue.java >> next()
@UnsupportedAppUsage
Message next() {
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();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
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 {
...
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
}
...
}
}
复制代码
代码简略了仍是有点多,别着急,慢慢看
那pre
何时就是0
了呢?
答:quit()
了以后
看下源码,Looper.java
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
复制代码
能够看到只是一个传参不一样而已,下面看看这个参数是干吗的
看下源码,MessageQueue.java >> quit()
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
复制代码
能够看到,safe == true
,就移除将来的Message
safe == false
,就移除全部的Message
mQuiting
变成了true
,记住他咱们一下子会用到
而改变ptr的地方在这里
在next()
里面
里面有个dispose
,找不到能够ctrl+F
找一下
这里只有在mQuiting == true
的时候,才会调用
这就是改mPtr
的地方,而后下次next()
的时候就会返回null
了
咱们已经知道了在Looper
的死循环里面,会将next()
返回的msg
交给Handler
,调用dispatchMessage()
dispatchMessage()
里面会先判断msg
是否是被post
过来的,由于post
要执行的逻辑在msg.callback
里面,callback
是一个Runnable
,这可能不是很好理解
你能够想一想runOnUIThread(Runnable)
,这里的Runnable
就是上面的callback
, 他们都是调用了Handler.post(Runnable)
至于为啥起个名叫callback
,我也纳闷儿
这些msg
是会的逻辑是你重写的handleMessage
那里的逻辑
若是实现了Handler.Callback
这个Interface
,就会处理mCallback
的handleMessage
而不是Handler
本身的handleMessage
这是一个优先级策略,没什么好奇怪的
咱们看下源码 => Handler.java >> dispatchMessage()
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
这就是Handler的消息机制了
接下来咱们讲讲Handler
的另外一个功能,切线程
Handler切线程使用的是ThreadLocal
ThreadLocal
是线程里面的一个数据储存类,用法相似map
,key
就是thread
可是他没有提供,根据key
来找ThreadLocal
的Values
的方法,因此暴露的api
就只能让你去get
当前线程的ThreadLocal
的Values
对象而已,就是key
——你本身无法做为参数传进去,只能是currentThread
若是你没用过ThreadLocal
,我给你举个例子
fun main() {
val booleanThreadLocal = ThreadLocal<Boolean>()
booleanThreadLocal.set(true)
println("in Thread[${Thread.currentThread().name}] booleanThreadLocal value = ${booleanThreadLocal.get()}")
thread(name = "thread#001") {
booleanThreadLocal.set(false)
println("in Thread[${Thread.currentThread().name}] booleanThreadLocal value = ${booleanThreadLocal.get()}")
}
thread(name = "thread#002") {
println("in Thread[${Thread.currentThread().name}] booleanThreadLocal value = ${booleanThreadLocal.get()}")
}
}
复制代码
结果是这样的:你能够本身运行看看
in Thread[main] booleanThreadLocal value = true
in Thread[thread#001] booleanThreadLocal value = false
in Thread[thread#002] booleanThreadLocal value = null
复制代码
话说回来,Handler怎么经过ThreadLocal切线程的呢?
答案是:Looper
是放在ThreadLocal
里的
回顾片头的流程,Handler
将消息插入MessageQueue
,而后Looper
取出来,再还给Handler
,这种设计不止是为了让msg
能够按顺序处理,还可让外部接口只有Handler
最关键的是,Looper
跟Handler
的触发关系只有Looper
触发Handler
,Handler
不会触发Looper
所以Handler
把消息放在MessageQueue
以后,就在等着Looper
来给本身派发任务(msg
)
举个例子:
线程A调用主线程的Handler
发一个消息
Handler
将这个消息插入MessageQueue
,此时其实还在线程A里
只有Looper
在next()
调用msg.target.dispatchMessage()
时,就变成了主线程了
仅仅是由于Looper
在 主线程 而已