前面介绍了线程的基本概念以及状态,这里来介绍Android中多线程通信Handler机制。
概述
总结一下自己从书上看的内容:
首先Handler通过send或post来将Message放入MessageQueue中,Looper通过loop来不断的轮循MessageQeueue里面的Message交给Handler处理。
*每个Thread只对应一个Looper
*每个Looper只对应一个MessageQueue
*每个MessageQueue中有N个Message
*每个Message最多指定一个Handler来处理
刚开始学有些奇怪,为什么Handler要sendMessage,然后由Looper将MessageQueue里面的消息发送过来,又交由Handler自己处理呢?自己直接处理不好吗?
这就好比你(Handler)接收到一个任务(Message),然后任务又不紧急但又必须处理,这样你就可以由该任务订个日程(MessageQueue),然后日程闹钟响起的时候,你就知道要来处理这个任务。
来看看自己创建一个线程利用Handler机制,需要怎么做
class MyThread extends Thread {
public Handler handler;
public void run() {
Looper.prepare();
handler = new Handler() {
public void handleMessage(Message msg) { }
};
Looper.loop();
}
}
首先看到的是Looper.prepare(),然后Looper.loop(),来看看Looper里面源码
public final class Looper {
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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 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;
}
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
}
可以看到prepare执行了Looper构造方法,将Looper对象保存到ThreadLocal当中。
ThreadLocal是一个特殊的全局变量,因为它的全局性只限于自己所在的县城,而外界所有的线程(即便是同一进程)一概无法访问到它,由这也可知每个线程的Looper是独立的。
Looper构造方法还创建了一个MessageQueue对象,应该早有预料,loop()方法显而易见就是不断轮循MessageQueue取出其中的Message来交给Handler(msg.target)处理
看完Looper就来看看Handler的源码
public class Handler {
public void handleMessage(Message msg) {}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
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 = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
}
在写应用程序时一般new出一个Handler对象都会重新写他的handlemessage方法(或者通过callback)在执行真正要做的任务,除此之外,我们还会通过Handler.sendMessage()这个方法来把发送消息,由源码跟进可知最后都会调用sendMessageAtTime方法,而这个方法就是一开始所说的msg压入MessageQueue。其中还看到msg.target=this这条语句,在Looper.loop()中看到msg.target.dispatchMessage(msg),这就是要发送消息给handler了,源码最终调用的是我们重写的handlemessage方法。这样一切都理清楚了。
MesasgeQueue里面出入队列等操作,学过数据结构理解起来也是很轻松
HandlerThread代码
public class HandlerThread extends Thread {
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
}
由HandlerThread源码可知这是一个封装了Handler机制的线程,并且不用手动的调用Looper等方法,其中使用wait/notifyAll解决了多线程中子线程1获取子线程2的Looper对象为空的问题。这样便成功避免了一些异常等问题。
我们应该如何应用
mThread = new HandlerThread("handler_thread");
mThread.start();
mWorkHandler = new Handler(mThread.getLooper());
这样就可以在子线程应用Handler了
网友评论