概述
Handler主要用于线程间
的通信,Handler主要是由MessageQueue
,Message
,Looper
,Handler
,共同组成,称为Handler消息机制,存储Looper使用了ThreadLocal
,下面我们一次讲解这几个类
Handler
主要负责发送消息,和处理消息MessageQueue
主要负责储存消息Looper
主要负责从MessageQueue
中取出消息,然后分发给Handler
ThreadLocal
主要负责存储不同线程的Looper
对象Message
主要负责存储数据
ThreadLocal
ThreadLocal
是一个线程内部的数据储存类,通过他可以在指定线程中储存数据,数据存储后,只有指定线程才可以可以获取储存数据,对于其他线程来说,则无法获取到数据;一般来说当某些数据是以线程为作用域,且不同线程有不同副本的时候,就可以考虑采用ThreadLocal
,比如对于Handler
来说,他们需要获取不同线程的Lopper
,这个时候就需要通过ThreadLocal
可以轻松在不同线程存储Looper
ThreadLocal
另一个使用场景是复杂逻辑的对象传递,比如监听器传递,有时候一个线程的任务过于复杂,这可能表现为函数作用栈比较深,以及代码入口的多样性,在这种情况下,我们需要监听器贯穿整个线程,这个时候就可以采用ThreadLocal
,让监听器作为线程的全局对象而存在,线程内只要get就可以获取监听器
ThreadLocal的使用
mThreadLocal = new ThreadLocal<>();
mThreadLocal.set(true);
Log.d("mmm","当前线程"+Thread.currentThread()+"ThreadLocal存储"+ mThreadLocal.get());
new Thread("thread1"){
@Override
public void run() {
super.run();
mThreadLocal.set(false);
Log.d("mmm","当前线程"+Thread.currentThread()+"ThreadLocal存储"+ mThreadLocal.get());
}
}.start();
new Thread("thread2"){
@Override
public void run() {
super.run();
Log.d("mmm","当前线程"+Thread.currentThread()+"ThreadLocal存储"+ mThreadLocal.get());
}
}.start();
我在主线程设置了true
, thread1
设置了false
, thread2
没有设置,按照正常来说获取,主线程 为true
,thread1是false, thraed2为null
,看一下log
09-28 11:30:12.616 32536-32536/com.example.jh.rxhapp D/mmm: 当前线程Thread[main,5,main]ThreadLocal存储true
09-28 11:30:12.618 32536-32745/com.example.jh.rxhapp D/mmm: 当前线程Thread[thread2,5,main]ThreadLocal存储null
09-28 11:30:12.619 32536-32744/com.example.jh.rxhapp D/mmm: 当前线程Thread[thread1,5,main]ThreadLocal存储false
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);
}
set
方法就是通过当前线程获取一个ThreadLocalMap
,然后通过ThreadLocalMap
去储存数据,如果ThreadLocalMap
是null
那么久同过当前thread
去创建一个ThreadLocalMap
,再去存储,下面我们看一下ThreadLocalMap
是如何创建的
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
class Thread implements Runnable {
...
ThreadLocal.ThreadLocalMap threadLocals = null;
}
每一个Thread
内部都有一个ThreadLocalMap
对象,如果这个对象为null,就为他重新赋值,然后我们看他是如何set
数据的
private void set(ThreadLocal key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
首先用key
计算出数组下标,然后从Entry[]
中取出值,如果有数据则重新赋值,如果没有数据,则创建一个新的Entry
添加到Entry[]
数组中
下面我们看一下get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
首先获取此线程的ThreadLocalMap
,如果不为Null
,就用key
计算出Entry[]
数组下标,然后取出Entry
,然后再取出具体的值,如果ThreadLocalMap为Null
或者取出的Entry为Null
,就重新赋值
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
ThreadLocal总结
每一个线程中都会有一个 ThreadLocal.ThreadLocalMap threadLocals = null;
成员变量,我们操作ThreadLocal的set个get方法
时,都是操作的单个线程中ThreadLocalMap
对象,而ThreadLocalMap
中是以Entry[]
数组来储存数据,所以就实现了每个线程都会有不同的值
Lopper
创建Lopper
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//一个线程只允许创建一个looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
利用静态prepare
方法,来创建Looper
,对于无参的情况,默认调用 prepare(true)
,表示Looper
允许退出,false
表示不允许退出,一个线程只允许创建一个Looper
,Looper
储存在ThreadLocal
中,这样就实现了一个线程一个Looper,创建Looper的时候还创建一个MessageQueue
prepareMainLooper
该方法主要在ActiityThread只使用,创建主线程的Looper
public static void prepareMainLooper() {
//该Looper不允许退出
prepare(false);
synchronized (Looper.class) {
//把该Looper设置为主线程Looper,只能设置一次
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//获取主线程的Looper
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
loop()
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.");
}
//获取MessageQueue
final MessageQueue queue = me.mQueue;
...
for (;;) {
//从MessageQueue中取出消息,没有消息就会阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// 一般情况msg不会为null,只有messageQueue退出,msg才会返回null
return;
}
...
//msg.target其实就是Handler对象,把消息分发给Handler
msg.target.dispatchMessage(msg);
...
//把Message放入消息池
msg.recycleUnchecked();
}
}
loop()
方法进入无限循环,不断重复以下操作
- 从
MessageQueue
中取出Message
- 把
Message
分发给对应的Handler
- 把分发后的
Message
回收到消息池,以便重新利用
quit()
public void quit() {
//移除消息
mQueue.quit(false);
}
public void quitSafely() {
//安全的移除消息
mQueue.quit(true);
}
Looper.quit()
,最终调用的是MessageQueue
的quit
方法,这俩个方法区别就是,一个quit
方法会,直接退出,quitSafely
会执行完剩余的消息退出
Looper总结
Looper
的主要工作是,从MessageQueue
中获取消息,然后分发给对应的Handler
,除了主线程,其他线程都需要自己去调用Looper.prepare()
方法创建Looper
,因为主线程的Looper
在ActivityThread
的main
方法里面创建了,创建完成之后在调用Looper.loop
方法进行循环,下面是一个创建Looper
的经典例子
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Handler
构造方法
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
...
//获取此线程中的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;
//是否设置了Callback
mCallback = callback;
//是否为异步
mAsynchronous = async;
}
这个几个构造方法,最终都调用了俩个参数的构造方法,对于无参的构造方法,默认使用本当前线程中的looper
,callback
为null
,消息为同步处理的方式
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
以Looper
为参数的构造方法,可以指定Looper
发送消息
这是发送消息的调用链,我们发现最终都是调用了MessageQueue.enqueueMessage()
send
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + 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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//在这里为msg.target赋值
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
post
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;
}
Handler.sendEmptyMessage()
系列方法,最终调用了MessageQueue.enqueueMessage(msg, uptimeMillis)
,将消息添加到消息队列中,其中uptimeMillis
是系统时间加上延迟时间
分发消息
在Looper.loop()方法中,发现有消息,会调用msg.target.dispatchMessage方法,来分发消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
分发流程
- 当
msg
中有callback
时,则调用message.callback.run()
;方法,其中的callback
指的Runnable
- 如果
callback
为空,那么则看一下成员变量的mCallback
是否为空,这个是Handler
的构造方法传入的 - 如果
mCallback
也为空,则调用handleMessage
方法,这个一般在Handler
的子类中重写
其他方法
removeMessages
移除消息,其实还是操作的MessageQueue,下面再一起分析
public final void removeMessages(int what, Object object) {
mQueue.removeMessages(this, what, object);
}
Handler总结
Handler主要工作就是,发送消息,最终是把消息插入到了MessageQueue中,然后通过Looper.loop方法,循环从MessageQueue拿出消息,然后通过Handler把消息分发出去,这就完成了一次循环
MessageQueue
MessageQueue是java层和c++层链接的纽带,大部分的核心方法都是交给native层去做,MessageQueue中的native方法如下
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
想要详细了解这些native方法做了什么情移步到gityuan
大神的博客 Android消息机制2-Handler(Native层)
创建MessageQueue
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//通过native代码初始化消息队列
mPtr = nativeInit();
}
enqueueMessage 插入消息
boolean enqueueMessage(Message msg, long when) {
//msg.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) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 如果p==null表示消息队列为空,或者msg消息触发时间为队列最早,则把消息插入头部,如果阻塞唤醒队列
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//按照时间顺序插入到队列中,不需要唤醒队列
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;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
MessageQueue的插入,其实就是链表的插入,是按照Message的触发时间先后顺序排列的,消息头是最早触发的,当有消息假如队列时,会从头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序
next 获取消息
Message next() {
final long ptr = mPtr;
//如果消息循环已经退出就直接返回null
if (ptr == 0) {
return null;
}
// 注意这里首次循环为-1
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,等待nextPollTimeoutMillis时长,或者被唤醒都会返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
...
if (msg != null) {
if (now < msg.when) {
//当前时间小于下个消息测触发时间,就重新设置阻塞的时间
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);
msg.markInUse();
return msg;
}
} else {
//没有消息则把nextPollTimeoutMillis设置为-1
nextPollTimeoutMillis = -1;
}
// 如果消息正在推出则返回null
if (mQuitting) {
dispose();
return null;
}
//这里是idlehandler,注意这里pendingIdleHandlerCount < 0才会进入,而等于0不会进入,什么时候小于0呢,其实就是第一次进入循环,赋值为-1
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//注意这里pendingIdleHandlerCount <= 0,小于等于0就直接continue,不会走下面的代码
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
//上方是源码注释,意思是,运行IdleHandler,但是只会在第一次迭代运行
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
//上边是源码的注释,意思就是把它重新赋值为0,也就意味着IdleHandler只执行一次
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
-
首先进入先判断是否已经退出,退出直接返回,不退出进行下一步
-
之后再判断当前的
MessageQueue
是否为空,为空则赋值阻塞时间nextPollTimeoutMillis = -1;
-
如果不为空,则判断当前时间是否大于等于消息的触发时间,如果小于触发时间,则赋值阻塞时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
-
如果当前时间大于等于触发时间,则直接取出消息返回,并且把此消息移除队列
-
其中涉及一个方法
nativePollOnce(ptr, nextPollTimeoutMillis);
这是一个native方法,主要作用是阻塞,nextPollTimeoutMillis
代表阻塞时间- 其中
nextPollTimeoutMillis=-1
表示,一直阻塞,直到被唤醒 - 其中
nextPollTimeoutMillis=0
表示,不阻塞,立即返回 - 其中
nextPollTimeoutMillis>0
表示,阻塞nextPollTimeoutMillis
毫秒,如果期间唤醒也会立即返回
- 其中
IdleHandler
上方的源码继续向下分析就是IdleHandler
,我之前写的一篇文章Android LeakCanary的使用和原理
,LeakCanary中使用了IdleHandler
void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
IdleHandler的作用是在当前线程消息队列空闲时,去做一些我们想要做的操作,但是IdleHandler只会执行一次,上面注释已经描述的很清楚了
Message
Message主要包括以下信息
数据类型 | 成员变量 | 解释 |
---|---|---|
int | what | 消息类别 |
long | when | 消息触发时间 |
int | arg1 | 参数1 |
int | arg2 | 参数2 |
Object | obj | 消息内容 |
Handler | target | 消息响应方 |
Runnable | callback | 回调方法 |
消息池
Message
维护了一个消息池,recycle()
方法可以把用过的消息假如到消息池中,这样做的好处是,当消息池不为空时,可以直接从中取出Message
使用,而不是重新创建,提高效率
静态变量sPool
的数据类型是Message
,其实是一个链表,维护这个消息池,MAX_POOL_SIZE
代表容量,默认50
recycle()
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
其实就是一个链表的插入,把信息清除,然后插入
obtain() 从消息池中获取消息
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
如果sPool不为null,就从池子了取出一个Message,如果为null,就直接New一个返回
Android中为什么主线程不会因为Looper.loop()里的死循环卡死
要完全彻底理解这个问题,需要准备以下4方面的知识:Process/Thread
,Android Binder IPC
,Handler/Looper/MessageQueue消息机制
,Linux pipe/epoll机制
给大家推荐一个靠谱答案,还是gityuan大神的回答Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
总结
- Handler通过sendMessage方法发送消息,插入到MessageQueue
- Looper通过loop方法循环取出Message,然后分发给Handler
- 然后通过dispatchMessage,交给相应的方法做处理
参考:http://gityuan.com/2015/12/26/handler-message-framework/
Android 开发艺术探索