1、前言
- 在
Android
中,Handler
消息机制十分常见,在实现多线程消息切换的场景屡屡能看到,比如子线程切换到主线程更新UI
操作、系统源码层也经常用到Handler
消息机制,所以Handler
显得格外的重要,开发者必学的一个知识点,不仅只会使用,还要深入源码了解其工作过程,正所谓“知其然,知其所以然”; - 通过本篇文章对
Handler
机制进行一个详细的分析和解读,有什么不对的还望各位大佬指教;
2、了解Handler消息机制
1、为什么要使用Handler呢?在Android中,由于系统是默认不能在子线程对UI进行更新操作,那么就需要借助Handler机制进行消息异步切换,实际上就是为了避免线程操作不安全的问题,假如你没明白为什么子线程默认不能操作UI界面,可以看看这篇文章:Android:为什么子线程不能更新UI
-
Message
:消息的载体; -
MessageQueue
:消息队列,基于单链表的数据结构存放Message
对象; -
Looper
:消息泵,不断的从MessageQueue
消息队列中抽取消息Message
发送到Handler处理; -
Handler
:消息的处理者;
那么我们就依次分析这四个组件涉及的源码以及工作原理
3、源码解析
要分析Handler
消息机制工作原理,我们可以从Handler的创建为入口:
Handler Handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
实例化Handler
,并且重写handleMessage
方法,进入其内部观察:
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()
获取了Looper
对象,假如Looper
为空,会抛"Can't create handler inside thread that has not called Looper.prepare()"
异常;进入Looper.myLooper()
方法中查看:
//sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到,Looper
对象是从ThreadLocal
对象取出来的,简单来说一个线程对应一个ThreadLocal
对象,一个ThreadLocal
对象对应一个Looper
,从而保证一个线程具有唯一的Looper
对象,假如你不了解ThreadLocal
,可以看看这篇文章Android:ThreadLocal源码解析
从源码的注释看出,要想Looper
不为null
,那么先调用prepare()
方法来创建Looper
对象,但是好像创建Handler
的时候并没有调用啊?通过分析明白在主线程默认在程序入口main()
方法已经执行创建Looper
的操作了,那么我们转移到ActivityThread.main()
中:
public static void main(String[] args) {
···
Looper.prepareMainLooper();
Looper.loop();
···
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到在main
方法中,调用了Looper.prepareMainLooper()
,该方法再调用了Looper.prepare
方法;
值得注意的是,系统在主线程才会默认调用
Looper.prepare
方法,而在子线程必须手动调用,否则会报错;
在prepare
方法中,实现了Looper
的实例化,并且保存到ThreadLocal``对象中,以便下次直接获取; 在
Looper的构造方法中可以看到实例化了
MessageQueue消息队列,这样也就说明了
MessageQueue对象在一个线程中是唯一的; 现在有了消息泵和消息队列那么就可以开始轮询了吧,回到
main方法中调用
Looper.loop()开启轮询,继续查看
Looper.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;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
···
msg.target.dispatchMessage(msg);
}
}
可以看到先获取之前已经实例化的Looper
对象,然后取出MessageQueue
对象,然后开启一个for
无限循环,调用next()
获取消息体,假如没有消息就阻塞,有消息的话继续调用msg.target.dispatchMessage(msg)
,实际上Message
的target
对象就是Handler
,然后继续查看Handler
的dispatchMessage()
:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
public void handleMessage(Message msg) {
}
可以看到内部执行了handleMessage
方法,该方法就是创建Handler时重写的方法,是个空实现,开发者可以在该方法处理消息;
那么知道了Handler
、Looper
、MessageQueue
的创建和工作过程之后,不要忘了Message
消息载体:
//Message message = new Message();
Message message = Message.obtain();
message.arg1 = 1;
message.obj = "AA";
handler.sendMessage(message);
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
Message
的创建推荐使用Message.obtain()
,从源码可知直接从消息池中取,避免消息重复创建造成的资源浪费;消息创建好后,调用Handler.sendMessage()
,我们继续查看该源码:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,通过一系列的方法,最终调用enqueueMessage
方法,在该方法中可以看到msg.target = this
,这句代码的意思就是把当前Handler
赋值给msg.target
变量中,也印证了之前说的,然后执行MessageQueue. enqueueMessage()
,我们转移到该方法中:
boolean enqueueMessage(Message msg, long when) {
···
synchronized (this) {
···
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 {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
该方法内部实现了按时间把消息插入队列中,采用单链表实现,提高插入、删除消息的效率;
通过以上的源码分析,现在再梳理一遍,
当Handler.sendMessage()
发送消息,会通过MessageQueue.enqeueMessage()
向消息队列添加一条消息;Looper.loop()
开启消息轮询,并不断的调用MessageQueue.next()
取出消息;通过Handler.dispatchMessage
发送给Handler
;Handler
获取消息后调用Handler.handlerMessage()
处理消息。
4、Handler.post(new Runnale)
其实在Handler
消息机制中,还有一种发送消息的方式:
handler.post(new Runnable() {
@Override
public void run() {
}
});
现在让我们来看看它是一个怎样的流程:
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;
}
可以看到使用post
方式发送消息,也是调用来sendMessageDelayed
方法,只不过在getPostMessage
方法中为Message.callback
赋值来,可以猜想是为了回调使用的,通过上面的源码分析,会发现:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
在调用dispatchMessage
方法的时候,是先优先判断Message.callback
是否为空的,如果使用了post
方式发送消息,那callback
不为空,就会调用handleCallback
方法,内部回调了run
方法;
5、子线程使用Handler
既然主线程可以使用Handler
,那么子线程一样可以;但是要注意的是,在创建Handler
之前要调用Looper.prepare
方法创建Looper
对象,调用Looper.loop
方法开启消息轮询,如以下代码:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Looper.loop();
}
}).start();
6、总结
- 通过对
Handler
的源码分析,理清了Message
、MessageQueue
、Looper
、Handler
是在何时创建、何时建立关联、如何配合完成工作流程的; - 理清了
Handler.post
方式发送消息的工作原理; - 子线程中使用
Handler
的方法