上一章的学习我们已经了解了Handler进行线程间通信的使用流程,我们知道,有几个比较重要的概念需要我们重点去了解,它们分别是:
概念 | 定义 | 作用 |
---|---|---|
主线程(UI Thread) | 应用程序启动时,默认开启UI线程 | 处理UI相关操作 |
子线程(Worker Thread) | 人为控制开启的线程 | 处理耗时操作(网络请求、数据加载等) |
消息(Message) | 线程间通讯的数据载体 | 存储线程间需要传递的信息 |
消息队列(MessageQueue) | 存储Message的一个数据结构(先进先出FIFO) | 存储Handler发送的消息 |
处理器(Handler) | 线程通信媒介、线程间消息处理器 | 发送消息到消息队列、处理Looper分发的消息 |
轮询器(Looper) | 消息队列和处理器的通信桥梁 | 循环取出消息队列中的消息、分发消息给相应的handler |
简单剖析
//定义handler
Handler handler = new Handler();
//发送消息
handler.post(new Runnable() {
@Override
public void run() {
//do something
}
});
首先我们打开Handler的无参构造器源码,调用了两个参数的构造方法,如下:
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;
}
可以看到先调用了Looper的myLooper方法,并判断了获取对象是否为null,我们继续来看Looper 代码:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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));
}
这里牵涉到了ThreadLocal的相关内容,我对这块了解的也不是很多,所以就不献丑了,以后可能会针对Java多线程专门做研究,这里我们只需要知道myLooper返回的对象如果为空,说明该线程没有初始化Looper,handler使用会报错,那么为什么我们在主线程中使用Handler没有出问题呢,这是因为我们来看这些代码:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
我们追溯这行代码可以看到在ActivityThread类的main方法中调用了此代码,主线程的Looper就是在这里进行绑定的,这也就是为什么我们可以正常使用handler的原因。
接下来我们来看第二部分代码,也就是handler.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;
}
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);
}
可以看到,该方法实质上还是调用了sendMessage方法,只不过传入的Runnable对象设置为Message的callback。从这过程中也能看出,如果此线程的Looper未能初始化,那么MessageQueue也为空,在sendMessageAtTime方法中依然会报错。下面我们来看看enqueueMessage方法,看名字就是知道是一个入队的方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到在这个方法里给传入的Message设置了Target属性为当前Handler,并将消息根据消息时间放入消息队列中。
到这里我们发送的消息就已经放入消息队列中了,那么怎样取出消息并且发送给Handler呢,我们继续打开Looper的代码并找到这一行:
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
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();
}
}
我们刚刚在查看ActivityThread代码是可以发现main方法中调用了Looper.loop方法,从方法名上看,这是一个循环,从代码看也确实如此。这里有一个无限循环方法,不断地从MessageQueue取出消息,若消息为空,则阻塞,若消息不为空则调用了Message中target的dispatchMessage方法,消费该message后,回收。我们知道这个target也就是Handler,我们来看看这个方法做了哪些事情:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这里做了一个判断,在Message有回调方法时,使用Message的回调方法来处理,若回调方法不存在,则调用handleMessage并将message传入此方法,进行进一步处理。
在这个例子里我们知道message的callback是我们传入的runnable,那么会执行handleCallback方法,也就是:
private static void handleCallback(Message message) {
message.callback.run();
}
如果是我们上一节中的写法,会调用handleMessage方法,也就是我们自己复写的方法,会执行我们自己写的逻辑流程。
至此,我们的流程分析就结束了,当然,只是从皮毛上对Handler 进行了解释,我们这里进行总结:
子线程向主线程发送消息:
- 主线程中创建Handler,并复写相应方法,或使用post(Runnable)方式,写下逻辑代码
- 子线程中使用Handler的sendMessage或post发出消息
- Handler内部处理消息,并将消息传递给MessageQueue,MessageQueue根据消息执行时间进行相应的入队操作
- Looper不断轮询从MessageQueue中取出消息,如无消息,就阻塞,若有消息,将消息分发给相应的Handler
- Handler收到分发的消息,使用相应的方法进行处理,到这里子线程与主线程通信结束
ps:还有几点重要的补充知识:
子线程若要向子线程发送消息,需要在子线程中调用Looper.prepare、Looper.loop才能使用Handler,不然会报错
一个线程中只能有一个Looper和一个MessageQueue,但可以有多个Handler
好了,这一章的内容就是这些,下一节我们来学习下Android中为解决多线程通信而提供给我们使用的HandlerThread,敬请期待~
我的个人博客,欢迎来访~
enjoy~