Handler到底是如何完成线程切换的?这个问题要从Handler最初的用法和原理讲起。
首先我们列出正常情况下一个Handler使用的步骤然后讲解分析如何实现,这里不对一些基础的概念做解释,具体的请查阅源码。
Handler的使用步骤:
1.调用Looper.prepare();
2.创建Handler对象;
3.调用Looper.Loop()方法。
4.线程中发送消息。
这样我们就创建好了一个Handler可以采用sendMessage等方法发送消息。
那么这几步都干了什么?
第一步:调用prepare方法
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对象保存在了一个ThreadLocal对象中,至于ThreadLocal是干什么的,查询有关ThreadLocal如何存储线程中数据的资料。
上面代码中创建了一个Loper对象,new Looper的时候我们可以看到:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// 创建了一个MessageQueue对象,这个就是一个消息队列用来保存我们发送的Message,其实这个MessageQUueue是一个单链表。
后边我们会知道这里创建了一个MessageQueue对象,从字面上来看就是一个消息队列,其实他是一个链表,链表的数据结构就是便于插入和删除。
第二步创建Handler
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
......
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从构造方法中我们知道了Hanlder中获取了一个Looper对象,这个Looper中保存了一个mQueue.这个Lopper对象就是我们前面保存在ThreadLocal中的那个。
第三步调用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;
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;
}
......
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {// 处理消息
msg.target.dispatchMessage(msg);
......
}
我们清楚的看到:loop()方法中获取了Looper对象,取出了消息队列mQueue,然后开始循环。同时调用了MessageQueue的next()方法取出一个消息最后调用了这个消息的dispathchMessage();看完源码我们知道这个msg.tagget.dispatchMessage()就是Handler中的消息处理的方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这个方法的代码中我们看到,首先判断了callback是不是null.这是因为Handler使用有两种方法,一种可以通过创建CallBack的方式。最后调用了Hanlder的handleMessage(msg);这个方法就是我们经常自己实现的消息处理方法。所以我们就到达了目标。
第四步:发送消息
调用sendMessage()等方法发送消息,追踪发送消息的方法,最后调用了以下的方法:
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构造方法保存的mQueue,然后调用了enqueueMessage()方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最后看queue.enqueueMessage(msg, uptimeMillis);在queue中处理了消息。
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {// 同步的操作,同一时间只能一个线程操作
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
......
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {// 判断消息要执行的时间顺序
// New head, wake up the event queue if blocked.
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;
}
}
return true;
}
也就是通过消息要执行的时间,最后把消息插入链表中。
这样我们就完成了消息的发送!
此刻:主线程的loop()方法一直在循环处理着消息,我们发送的消息就会到loop()方法中去,最后交给Handler处理。
那么到底是怎么样就完成了线程的切换呢?
其实就是这个消息发送的过程,我们在不同的线程发送消息,线程之间的资源是共享的。也就是任何变量在任何线程都可以修改,只要做并发操作就好了。上述代码中插入队列就是加锁的synchronized,Handler中我们使用的是同一个MessageQueue对象,同一时间只能一个线程对消息进行入队操作。消息存储到队列中后,主线程的Looper还在一直循环loop()处理。这样主线程就能拿到子线程存储的Message对象,在我们没有看见的时候完成了线程的切换。
所以总结来讲就是:
1.创建了一个Looper对象保存在ThreadLocal中。这个Looper同时持有一个MessageQueue对象。
2.创建Handler获取到Looper对象和MessageQueue对象。在调用sendMessage方法的时候在不同的线程(子线程)中把消息插入MessageQueue队列。
3.在主线程中(UI线程),调用Looper的loop()方法无限循环查询MessageQueue队列是否有消息保存了。有消息就取出来调用dispatchMessage()方法处理。这个方法最终调用了我们自己重写了消息处理方法handleMessage(msg);这样就完成消息从子线程到主线程的无声切换。
最后补充:我们经常使用Handler没有创建Looper调用Looper.pepare()和Looper.loop()是因为在ActivityThread中已经创建了主线程的Looper对象,保存在了ThreadLocal中,我们在创建Hander的时候就会从ThreadLocal中取出来这个Looper。
从主线程类ActivityThread的入口方法main()方法中我们可以清楚的看到系统帮我们如何实现的:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("");
Looper.prepareMainLooper();// 调用了prepareMainLooper(),创建Looper和MessageQueue
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();// 获取Handler
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();// 开始调用loop()方法
throw new RuntimeException("Main thread loop unexpectedly exited");
}