文章未仔细梳理
Handler 机制大致描述:
这个机制主要牵涉到类 Handler、Message、MessageQueue 和 Looper.
这个机制大致工作流程:
① 执行 Looper.prepare();方法,为当前线程准备Looper对象;
②接下来就是往消息队列中插入消息,也就是我们熟悉的handler.sendMessage(new Message()),我们每插入一条消息 sendMessage(msg),Looper.loop()方法里面就会取出来并分发。
③ 执行 Looper.loop();方法,这个方法里面有一个for 循环,一直从消息队列(MessageQueue)中取消息(Message),如果消息队列中存在消息,就从Message中取出target(Handler类型)执行msg.target.dispatchMessage(msg),最终执行handler.handleMessage(msg);
Handler机制源码分析:
1. Looper.prepare();
进入 Looper.prepare() 方法,调用prepare(true);
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
①可以看到static修饰的sThreadLocal成员变量,用于存放与线程相关的Looper对象(Looper是如何与线程关联的?下面讲解)
②如果 sThreadLocal.get()不等于null,则抛出异常,这里表明一个线程只能有一个Looper对象,一个线程不能多次准备 Looper,即在同一个线程里不能多次调用Looper.prepare();
③ sThreadLocal.set(new Looper(quitAllowed)),线程的Looper对象就是在这里设置的
1.1接下来分析sThreadLocal.set(new Looper(quitAllowed))源码
public class ThreadLocal{
...
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
...
}
①可以看到首句代码获取了当前线程对象 t --> 然后执行ThreadLocalMap map = getMap(t);(其实就是Thread中的成员threadLocals),
如果map不为null,就将value(我们传过来的looper对象)设置进去,
如果为null,就执行createMap(t, value)创建ThreadLocalMapd对象赋值给t.threadLocals,而firstValue(Looper对象)则传入ThreadLocalMapd中,这样当前线程就保存关联了一个looper对象。
--------------------------------------------------
2. 进入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();
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);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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);
}
msg.recycleUnchecked();
}
}
①在loop()方法中,首先执行 final Looper me = myLooper(); 这里获取当前线程的Looper对象,
public class Looper{
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
}
public class ThreadLocal{
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
}
在myLooper()方法中调用的是sThredLocal.get() (sThreadLocal前面已经出现过),
在get()方法中,首先获取当前线程,然后执行 ThreadLocalMap map = getMap(t);前面已经知道这个方法返回的是 Thread的成员threadLocals,这个成员是在Looper.prepare()中初始化的,并且它存放了一个Looper对象,所以sThredLocal.get()最终返回的是当前线程的Looper对象。
②回到我们的Looper.loop()继续往下看,如果 myLooper()返回null, 则说明并没有为当前线程准备Looper对象(没有执行Looper.prepare()方法),抛出异常。
然后执行 final MessageQueue queue = me.mQueue; 这里获取的是Looper中的消息队列(handler.setMessage(msg)消息插入的地方),
接下来就是一个for循环,一直在获取消息,并分发消息
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);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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);
}
msg.recycleUnchecked();
}
在Message msg = queue.next();这里,当消息队列里面没有消息时,next()方法也是阻塞的。
所以,在消息队列中没有消息时,线程是处于阻塞状态的。
为什么loop的死循环不会阻塞住主线程?
https://www.jianshu.com/p/8c829dc15950
https://www.jianshu.com/p/4fac27284475