为什么?
多问为什么是解决并理解问题的最好的办法,Handler是Android SDK中的类,Android为什么要引入Handler、Looper、MessageQueue呢?
线程间通讯,消息队列处理
经常使用的场景有通过Handler更新UI,貌似我们理解中更新UI只能在主线程
这句话好像是对的,其实更准确的说法是更新UI只能在UI线程,即要更新的UI所在ViewRoot创建时候的线程,如果当前ViewRoot是在子线程创建的,更新ViewRoot里面的UI必须在这个子线程中更新,而我们常见的ViewRoot创建都是在主线程,所以把常见的结果当成标准结果是万万不可的。
为什么采用消息队列的形式呢?消息队列的方式可以解决线程堵塞数据混乱等问题。
言归正传
Handler
Handler是用来发送和处理消息的
Handler对象的创建需要绑定一个指定线程的Looper, 即Handler是和线程相关的,如一个Handler绑定的是一个子线程中的Looper,那么这个Handler就不能用来更新UI线程(主线程)。
Looper
Looper是通过for (;;)
循环来不停的把MessageQueue中的消息交给Handler处理
MessageQueue
用来存储Message的消息队列
源码分析(类分析)
Handler的构造函数中只有2个基本构造函数
1.未指定Looper
public Handler(Callback callback, boolean async) {
//省略部分代码
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
2.指定Looper
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
其中未指定Looper通过mLooper = Looper.myLooper();
来获取当前线程中的Looper
让我们来看看Looper.myLooper();
究竟做了哪些操作 ,打开Looper
源码
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
额。。。sThreadLocal
是什么鬼,怎么通过他返回一个Looper
继续查看
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
原来是一个ThreadLocal
ThreadLocal用于保存某个线程共享变量,当前即为保存线程中Looper
变量,通过sThreadLocal
来获取当前线程的Looper
,在过sThreadLocal
没有进行set()
的时候sThreadLocal.get()
为空,为空的时候
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
会提示RuntimeException
提示我们在创建对象之前必须先执行Looper.prepare()
那为什么我们经常在使用Handler更新UI的时候没有执行过呢,其实并不是没有执行,只不过并不需要我们来执行,在ActivityThread
的main
方法中已经执行过Looper.prepareMainLooper();
所以不需要我们执行,系统已经帮我们执行过了
而在子线程中创建Handler 或指定的子线程中的Looper,必须我们手动执行Looper.prepare()
不然就会报RuntimeException
异常信息
继续查看Looper.prepare()
执行了哪些操作
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//如果已经存在了Looper也会报异常 所以Looper.prepare()只能必须执行1次
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//保存在sThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
继续看new Looper(quitAllowed)
的处理
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
创建MessageQueue对象 绑定当前线程
小结
自此三者的关系明朗了起来
Handler绑定了Looper,Looper创建了一个MessageQueue对象
Handler拥有Looper、MessageQueue成员变量
源码分析(如何发处理消息)
通过层层跳转,发送消息都会执行到sendMessageAtTime
方法中
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);
}
也就是enqueueMessage(queue, msg, uptimeMillis)
方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//设置msg的target为当前Handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里主要就是设置msg的target为当前Handler然后调用MessageQueue的enqueueMessage(msg, uptimeMillis)
方法把此msg存储到消息队列中
到了这里好像结束了,源码分析完了,等等还没消息处理呢,怎么只有发送,没有接收处理 自此Looper.loop()
方法登场了
public static void loop() {
//省略部分代码
for (;;) {
Message msg = queue.next(); // might block
//省略部分代码
try {
//执行msg的target(Handler)的dispatchMessage方法
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//省略部分代码
}
}
原来for循环不停的取出msg执行msg对于的Handler中的dispatchMessage(msg)
方法
public void dispatchMessage(Message msg) {
//如果msg创建的时候设置了callback则会执行callback的run方法
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果在创建Handler的时候设置了callback则会执行callback的handleMessage方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//都没有设置则会执行Handler的handleMessage方法
handleMessage(msg);
}
}
至此 Handler的发送与消息处理整个流程就解析完毕
总结
线程中使用Handler步骤
1.Looper.prepare()
准备Looper(主线程已经在ActivityThread中执行);
2.new Handler()
创建Handler(注意指定的Looper线程或创建对象所在的线程)
3.Looper.loop()
启动Looper 循环取出MessageQueue中的消息交给Handler处理(主线程已经在ActivityThread中执行)
发送消息流程
1.Handler.sendMessageAtTime()
定时发送消息
2.MessageQueue.enqueueMessage()
存储消息至消息队列
3.Handler.dispatchMessage(msg);
消息的分发处理
网友评论