什么是Handler机制
Android系统不允许子线程访问UI组件(子线程访问主线程),主要是因为UI控件是非线程安全的,在多线程中并发访问可能会导致UI控件处于不可预期的状态。而不对UI控件的访问加上锁机制的原因有:
- 上锁会让UI控件变得复杂和低效
- 上锁后会阻塞某些进程的执行
而且Android系统为了避免ANR异常,通常需要新启子线程来处理耗时操作,所以线程间的通信是很常见的开发场景。因此,为了解决子线程更新UI控件以及处理线程间的通信问题,系统提供了Handler机制。总的来说,Handler机制就是跨线程通信的消息传递机制。
简介
Handler消息机制主要有四个核心类:Message(消息)、MessageQueue(消息队列)、Looper(消息提取泵)、Handler(消息处理者)。它们之间具体协作流程如下:
Handler运行流程message(消息)
.Message的主要功能是进行消息的封装,同时可以指定消息的操作形式。我们类比生活中的邮寄信件来分析Message,Message对象就是我们要寄出的信件,它应该包括以下内容:
收件人(消息发送给谁处理):
- Handler target属性:
①通过Message.setTarget(Handler target)来设置,我们常用的Handler.sendMessage(Message msg )发送消息时,在调用Handler类的enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法时,也是设置Message对象的target属性。
// Handler.java 源码
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
②在调用Messenger.send(Message message)发送消息时,Messenger对象内部会封装一个Handler对象,这个handler对象就是Message的处理者。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Messenger messenger = new Messenger(handler);
messenger.send(message);
寄件人(对方怎么回信):
- public Messenger replyTo属性:
发送消息时设置message.replyTo属性,在消息被接受以后,接收者可以从消息对象的replyTo属性提取出一个Messenger对象。通过这个Messenger对象就可以发送"回信"。当然你也可以发送一个不带寄件人信息的匿名信件,所以replayTo不是必需设定的属性。
信件内容(要传递的信息或数据)
- Bundle data属性:通过Bundle来封装需要传递的数据;
- public Object obj属性:
- public int arg1属性:如果只需要存储几个整型数据,arg1 和 arg2是setData()的低成本替代品;
- public int arg2属性:如果只需要存储几个整型数据,arg1 和 arg2是setData()的低成本替代品。
信件ID(通过不同的ID,接收者做不同的业务处理)
- public int what属性:在设置what属性的时候,需要注意不同Handler之间what值冲突。
其他属性
- Message next属性:用来维护消息在消息队列当中的顺序(参见MessageQueue.enqueueMessage(Message msg, long when) 源码);
- Runnable callback属性:设置这个属性之后,在消息处理的时候将会拦截Handler.handleMessage(Message msg),转而执行callback的run()方法(参见Handler.dispatchMessage(Message msg)源码);
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();
}
MessageQueue(消息队列)
消息队列被封装到Looper里面了,我们一般不会直接与MessageQueue打交道。我们只需要记住它是用来存放消息的单链表结构。队列的顺序由Message的next属性来维护。MessageQueue是整个Handler机制的核心,里面涉及很多特性我们这里都不展开讲述(比如消息屏障机制)。可以扩展阅读深入理解MessageQueue、Handler之同步屏障机制(sync barrier)。这里我们只关心队列的入列和出列。
/**
* MessageQueue.java
* 往消息队列添加消息的函数
* @param msg 待添加的消息
* @param when uptimeMillis(系统开机运行时间)+delayMillis(延迟时间,由sendEmptyMessageDelayed设置)
* @return
*/
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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.
// 新建队列头,如果队列为空,或者有一个优先级更高的消息插入到队列头部(譬如使用sendMessageAtTime(message,0)),
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;
}
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 这是一个native方法,实际作用就是通过Native层的MessageQueue阻塞nextPollTimeoutMillis毫秒的时间。
// 1.如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
// 2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
// 3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
// Message设置的时间为到,将会阻塞等待。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
由上面的代码片段我们可以知道,在使用sendMessageAtTime(Message msg, long uptimeMillis)或者sendMessageDelayed(Message msg, long delayMillis)等方法的时候,我们传入的uptimeMillis和delayMillis并不能准确的设置消息的处理时间。执行的策略是:
①优先执行队列中靠前的Message;
②如果队列中最考前的Message还没准备好(SystemClock.uptimeMillis() < message.when),此时会阻塞等待。
Looper(消息提取泵)
Looper的核心作用就是通过Looper.loop()不断地从MessageQueue中抽取Message,并将消息分发给目标处理者。我们通过介绍Looper运行的三个步骤来掌握Looper的运行原理。
准备阶段(Looper.prepare())
我们知道整个消息处理机制的运作,就是将消息添加进消息队列以及从消息队列提前消息进行分发。Looper.prepare()就是用来初始化创建Looper对象和消息列表对象的函数。因此,Looper.prepare()是我们使用Handler机制时必不可少的第一步(注意我们平时在使用主线程的handler时,都是直接new Handler(),具体原因我们稍后分析)。
/**
* Looper.java
*/
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(每个Looper又只有一个消息队列),但是消息队列里面可以存放多个Message,而每个Message都可以通过.setTarget(Handler target)来设置自己的Handler处理者。这就是Handler机制中四大要素之间的数理关系。
- prepareMainLooper():顺带说一下这个方法,它的作用就是在UI线程(主线程)调用Looper.prepare(),并且会保留一个主线程Looper对象的静态引用。这个引用可以通过Looper.getMainLooper()来获取主线程Looper,方便跟子线程跟主线程通信。
启动和运行Looper.loop()
/**
* Looper.java
*/
public static void loop() {
// 获取当前线程的Looper对象
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.
// 根据MessageQueue里面的next()方法得知,要使得queue.next()返回null的情况只有两种
// 1:MessageQueue.mPtr == 0;这里的mPtr值是调用native方法创建MessageQueue之后,返回的MessageQueue的引用地址,通过这种方式将java层的对象与Native层的对象关联在一起
//MessageQueue.mPtr == 0说明消息队列创建失败;
// 2:MessageQueue.mQuitting == true;也就是调用Looper的quit()/quitSafely()方法之后,next方法会返回null
return;
}
//.........省略部分代码..........
try {
// 调用Handler的dispatchMessage(Message msg)方法处理消息
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//.........省略部分代码..........
msg.recycleUnchecked();
}
}
根据源码(配合上面的MessageQueue.next()方法的源码服用),我们可以清晰的看出来Looper.loop()方法的业务逻辑就是一个死循环不停的用MessageQueue.next()方法从消息队列中提取消息,并将消息交给target所持有的Handler进行处理(调用Handler.dispatchMessage(msg)方法)。
- 因为这里是死循环,所以线程的run()方法中,一般情况下Looper.loop()调用之后的代码逻辑不会被执行到(特殊情况,请参考下面的死循环退出条件)
private class MyThread extends Thread {
private Looper subLooper;
@Override
public void run() {
Looper.prepare();
subLooper = Looper.myLooper();
initHandler();
Looper.loop();
System.out.println("这句话永远不会被打印,除非消息队列初始化失败或者调用了Looper.quit()");
}
}
- 这里的死循环其实是有退出条件的MessageQueue.next()返回null的时候会return,循环终止。循环终止的情况有两种:
①MessageQueue.mPtr == 0;这里的mPtr值是调用native方法创建MessageQueue之后,返回的MessageQueue的引用地址,通过这种方式将java层的对象与Native层的对象关联在一起。MessageQueue.mPtr == 0说明消息队列创建失败;
②2:MessageQueue.mQuitting == true;也就是调用Looper的quit()/quitSafely()方法之后,next方法会返回null。 - 如果MessageQueue为空,消息列表没有消息,将会阻塞等待。
终止和退出quit()/quitSafely()
网友评论