根据消息机制,手写了一个简单的handler模型,便于理解消息机制的原理,源码在GitHub:https://github.com/renzhenming/MyHandler.git
Handler用于处理线程间通信,当我们需要在子线程更新UI时,通常是通过handler发送一个消息到主线程中,在主线程更新,代码如下:
//主线程创建Handler
private Handler mHander = new Handler(){
public void handleMessage(Message msg) {
mTextView.setText((String) msg.obj);
};
};
//子线程发送消息
new Thread(){
public void run() {
Message message = new Message();
message.obj = "哈哈哈";
mHander.sendMessage(message);
};
}.start();
那么这么一个过程是如何从sendMessage执行到handleMessage的,在看源码过程中,我们带着一个问题:
1)为什么Can't create handler inside thread that has not called Looper.prepare()这个异常会发生
从sendMessage开始看源码,有这么一个方法,uptimeMillis是将要发送消息的时间,这个时间是当前时间加上延迟的时间,这里可以看到获取了一个已经存在的MessageQueue,然后执行enqueueMessage把message加入队列中,我们看看这个MessageQueue是何时被创建的
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);
}
在发送消息之前,会首先创建Handler,那么我们看看Handler创建的额时候做了什么,找到Handler的构造方法
public Handler(Callback callback, boolean async) {
//针对handler如果没有设置为静态容易发生的内存泄漏进行log提醒
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());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
//这里找到了我们最初提出的那个问题,为什么会报
//这个错误,可以看出来,答案会在获取不到looper的情况下,
//所以我们需要重点看看myLooper做了什么
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从ThreadLocal中获取looper ,
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
从当前线程中获取到ThreadLocalMap 对象,然后从这个map中得到Looper对象,既然如此,如果可以从map中取到looper,那么之前可定有个将looper存入这个集合的过程,从上边的分析可以看出,如果取出的looper为null,也就是并没有将这个looper存入的话,就会抛出Can't create handler inside thread that has not called Looper.prepare()这个异常,并且提到了Looper.prepare方法,那么是不是在这个方法调用的时候,looper被保存的呢,这是一个猜想,我们可以看看源码
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();
}
看到这个prepare方法,方法执行获取到当前的线程对象,从线程对象获取到ThreadLocalMap对象,然后把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));
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
我们知道,一个线程中只有一个Looper,并且,上边说的这个异常也只有在子线程中创建Handler会发生,那么为什么在主线程创建可以不用调用Looper.prepare方法呢,这个要从应用启动说起,我们找到app启动的入口,ActivityThread类中的main方法,这两行代码是不是比较熟悉
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
Looper.loop();
......
}
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));
}
可以看到,其实在程序启动的时候,在主线程中就已经实例化了一个Looper了,这也就是为什么我们在主线程中创建handler不需要调用Looper.prepare的原因
然后我们再回到sendMessage的时候已经存在的那个MessageQueue的问题,它是何时创建的,刚才看到,当应用程序启动,会在主线程中实例化一个looper对象,而当Looper被实例化的时候,消息队列其实已经被同时实例化了
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
也就是说,程序启动后已经完成了这两步,第一,在主线程实例化了一个Looper对象,并且只有这一个Looper对象,looper实例化,主线程中唯一的消息队列也同时实例化了,这个MessageQueue也就是启动后就已经存在了
继续从这个方法往下看
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) {
//这个消息即将被加入消息队列,这个消息同时持有了
//发送这个消息的handler的引用,谁发送它,它就持有谁,
//target就是handler对象
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
......
msg.markInUse();
//message将要被发送的时间点
msg.when = when;
//第一次过来,mMessages是null
Message p = mMessages;
boolean needWake;
//这里会进行一些判断p=null表示是发送的第一条消息,
//when=0表示是这条消息不要延迟,
//when<p.when表示当第不是第一条消息时,比较这条消息的发送时间
//和上一条消息的发送时间,比上一条时间早
//满足上边三个条件,就走入下边的代码
if (p == null || when == 0 || when < p.when) {
//当地一条消息进来时,由于满足条件p=null,所以就设置
//这条消息的next,就是链表中下一条message为null
//假设没有第二条消息发送过来的话,这就是消息队列
//结束的标志,然后将第一条消息赋值给mMessages
//如果有下一条消息过来,这个mMessage就拿去和它做比较
//这行代码的意思就是如果下一条消息的时间早于上
//一条消息,就把上一条消息作为下一条消息的next存储
//把时间早的放在链表的前边
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.
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;
}
经过上边一系列的条件操作之后,会把所有发送的消息存放再链表中,并且按发送时间来排序,发送时间早的排在前边,发送晚的排在后边,到了这里,sendMessage的逻辑也就进行完了,它的作用只是将消息按照顺序放入队列而已,那么是什么时候从队列中取出消息回调到handleMessage中的呢?在子线程创建handler Looper.prepare和Looper.loop都是一块出现的,那么答案可能是在Looper.loop方法中
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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.");
}
//获取到当前线程的消息队列
final MessageQueue queue = me.mQueue;
......
for (;;) {
//通过一个死循环不断的从消息队列中取出下一个消息,然后执行
//发送这个消息的handler的handleMessage方法回调过去,到这里终于
//消息被收到了
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
.......
msg.recycleUnchecked();
}
}
主线程中同样是有loop方法的执行的,可以看到,在程序启动后,就有loop方法的调用,开启了一个无限循环不断的读取消息队列中的消息
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
Looper.loop();
......
}
总结一下消息机制的几点关键:
1)在一个线程(主线程或子线程)中创建Handler,第一步需要执行Looper.prepare,目的是初始化当前线程唯一的Looper对象和消息队列MessageQueue
2)当从当前线程send Message发送消息时,是将这个消息按照时间顺序加入消息队列
3)消息进入消息队列,要从队列中取出,需要执行Looper.loop方法,这是一个死循环,会一直执行,只要消息队列中存在消息,就将消息取出,发送到当前handler的handleMessage方法中
网友评论