Android中,当我们需要在子线程中进行网络请求等耗时操作后,如果需要更新UI时,通常会考虑使用Handler来处理,一种常用的写法如下:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
// update ui
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
}
});
除了上边我们用到的Handler类外,Android消息机制要正常工作还需要Looper和MessageQueue两个类的配合,下来我们分别介绍三个类,以及它们之间的工作原理:
1、MessageQueue
顾名思义就是消息队列,但其实MessageQueue的底层是通过单链表来实现的,MessageQueue是用来中转Handler对象发送出的消息,主要包括enqueueMessage和next两个操作,分别用来插入一条消息和取出一条消息,因为链表在插入和删除操作上有较高的效率,这可能就是使用该数据结构的原因吧。MessageQueue只负责保存消息,并不会处理消息,MessageQueue对象如何得到呢?继续往下看。
2、Looper
如果我们是在UI线程中使用Handler来发送异步消息,系统已经在当前UI线程通过Looper.prepareMainLooper()帮助我们创建好Looper对象,并调用Looper.loop()开启无限消息循环,不断从MessageQueue的实例中读取消息。如果在子线程中我们不手动创建Looper对象,则会抛出java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()的异常,所以在子线程中创建并使用Handler对象,就必须手动调用Looper.prepare()创建Looper对象,并通过Looper.loop()开启消息循环。
在子线程中可通过如下方式使用Android消息机制:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//创建Looper对象
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
//do something
}
});
Looper.loop();//开启消息循环
}
});
这样子线程同样具有了异步消息循环处理的能力。
我们先通过源码分析一下Looper.prepare()方法
public static void prepare() {
prepare(true);
}
很简单,只有一行代码,继续进入到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));
}
重点看一下new Looper()方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到在创建Looper对象时也创建了MessageQueue对象,此时Looper对象持有了MessageQueue对象的引用。并与当前线程绑定,保证一个线程只会有一个Looper对象,同时一个Looper对象也只有一个MessageQueue对象。
3、Handler
首先看一下Handler的构造方法
public Handler() {
this(null, false);
}
依然只有一行,继续跟进
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;
}
通过mLooper = Looper.myLooper(),我们Handler对象拿到了对应Looper对象的引用,产生了关联。
可以看到如果Looper等于null的话会抛出异常,这也验证了上边第二点中的说法,创建Handler对象时必须有Looper对象存在。
再看 mQueue = mLooper.mQueue这一行,其实就是将Handler的实例与我们Looper实例中创建的MessageQueue对象关联起来。此时MessageQueue对象已经和Looper对象以及Handler对象关联了起来,并且他们在同一线程中。
4、工作原理
结合文章开头的例子来分析 Handler 具体的工作原理。
通过Handler对象发送消息时可用的方法非常多,例如常用的sendMessage系列方法,post系列方法等,通过源码可以知道,这两系列方法都最终都是调用下边的方法来实现的:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
继续看下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);
}
其核心的的就是最后一行,跟进去
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
首先看第一行msg.target = this,将Handler对象赋值给的Message对象的target属性,此时Message对象持有了Handler对象的引用。
最后一行通过调用MessageQueue 对象的enqueueMessage方法来保存发送的消息。
上边Looper类中还有一个Looper.loop()方法没分析,调用该方法后,则会一直检测MessageQueue对象中是否有数据,有的话则取出,否则阻塞,此时我们来看一下源码:
public static void loop() {
........
省略
........
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);
........
省略
........
}
}
只看核心的代码,首先是一个无限的for循环,循环中调用MessageQueue 的next方法不断的取出Message对象,没有消息则进入阻塞状态。否则执行msg.target.dispatchMessage(msg),这里的msg.target就是上边enqueueMessage方法中的msg.target,也就是Handler对象的引用,所以此时消息就可以调用Handler的dispatchMessage()方法进行消息处理。此时Handler对象发出的消息在MessageQueue对象中通过 Looper 完成了中转,由于Looper是在主线程创建并开启消息循环的,所以dispatchMessage()在主线程被调用:
接下来看一下dispatchMessage方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先看第一行if判断,什么时候msg.callback != null成立呢,其实就是当我们通过如下形式发送消息时
handler.post(new Runnable() {
@Override
public void run() {
// update ui
}
});
继续查看post方法
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
进入getPostMessage方法中
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
记住m.callback = r这一行,此时再看dispatchMessage中第一个if条件的执行代码:
private static void handleCallback(Message message) {
message.callback.run();
}
方法中执行的正是callback 中的run方法
if (mCallback != null)什么时候成立呢?当我们通过如下方式使用Handler时成立
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
从而下边的代码得到执行
if (mCallback.handleMessage(msg)) {
return;
}
即就是我们上边new Handler.Callback()中的handleMessage方法
最后如果我们采用文章最开始的代码执行handler操作则会执行dispatchMessage中最后一行handleMessage(msg)方法:
public void handleMessage(Message msg) {}
可以看到是一个空的方法,因为需要我们自己去实现哦!同时 Handler 也是在主线程创建的,所以 handleMessage()
方法最终在主线程执行,这样就完成了从子线程到主线程的切换。
最后说一下,创建Message对象,可以通过new方法 ,也可以使用Message.obtain()方法,但建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。
到这里你有没有发现Looper
、MessageQueue
、Handler
之间的关系呢?
- 在Looper中创建了MessageQuene,Looper循环从MessageQuene取出消息,由于消息持有Handler的引用,最终会调用Handler的handleMessage()方法或其它方法,Looper会被保存在ThreadLocal中,一个线程对应唯一Looper。
- Handler发送消息时,会从ThreadLocal中取出对应线程的Looper,再从中得到对应的MessageQuene,发送的消息会持有Handler的引用,然后将消息保存到MessageQuene。
- 关于ThreadLocal在下一篇有讲到
到此Android消息机制的基本原理已经分析完了,简单的概括一下:通过Handler对象的相关方法将Message对象发送出去进而插入到MessageQueue对象中,然后Looper对象调用loop方法,进一步会执行MessageQueue对象的next方法取出消息,交给Handler对象的dispatchMessage方法来处理。这就是Android消息机制一个大致的流程。
网友评论