1.Android消息机制概述
1.1. Android消息机制是什么?
Android消息机制主要是指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作工程,这三者实际上是一个整体,只不过我们在开发过程中比较多的接触到Handler而已。
1.2. Handler的主要作用
Handler的主要作用是将一个任务切换到Handler所在的线程中执行。比如我们经常碰到的一个场景:Android建议不要在主线程中进行耗时操作,否则会导致程序无法响应(ANR),那么我们会在子线程中进行耗时的网络请求和I/O操作。当耗时操作完成后需要更新UI,由于Android开发规范的限制,我们不能在子线程中访问UI,否则会程序异常,这个时候就需要通过Handler将任务切换到主线程进行UI更新操作。
对于访问UI只能在主线程中进行,否则程序会抛出异常这个验证工作是由ViewRootImpl的checkThread方法来完成的,如下所示:
void checkThread(){
if (mThread != Thread.currentThread()){
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views."
);
}
}
- 这里延伸一个问题,系统为什么不允许在子线程中访问UI?
系统为什么不允许在子线程中访问UI呢?这是因为Android饿UI控件不是线程安全的,如果在多线程中并非访问可能会导致UI控件处于不可预期的状态,那为什么系统不对UI控件的访问加上锁机制呢?缺点有两个:首先加上锁机制会让UI访问的逻辑变得复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。鉴于这两个缺点,最简单高效的方法就是采用单线程模型来处理UI操作,对于开发者来说也不是很麻烦,只是需要通过Handler切换一下UI访问的执行线程即可。
2. MeesageQueue的工作原理
MessageQueue主要包含两个操作:插入和读取,分别对应MessageQueue的enqueueMessage和next方法。Handler通过send方法发送一条消息后,最终会调用MessageQueue的enqueueMessage方法,enqueueMessage方法会将这条消息插入到消息队列中。而next方法的作用是从消息队列中取出一条消息并将其从消息队列中移除。
2.1. enqueueMessage方法分析
我们先来分析下插入操作的实现,也就是enqueueMessage方法的实现,源码如下:
boolean enqueueMessage(Message msg, long when) {
//...省略无关代码
synchronized (this) {
//...
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 {
// 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;
}
从enqueueMessage的实现来看,它的主要操作是单链表的插入操作。链表的插入操作是按照when的大小进行排序,when值小的message排在前面:
if (p == null || when == 0 || when < p.when):
-
p == null:表示当前链表没有消息
-
when == 0:表示新Message的when为0
-
when < p.when:表示新Message的when小于链表首结点Message的when
-
如果是上述三种情况,则直接将新的Message插入到链表首部。
-
如果p == null || when == 0 || when < p.when为false,则会开始遍历链表,获取链表中的下一个Message与新Message的when值比较,如果满足p == null || when < p.when则结束循环,将新Message插入。
2.2. next方法分析
接着我们看下如何从消息队列中取出消息,也就是next方法的实现,源码如下:
Message next() {
//...省略无关代码
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (; ; ) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
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.
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;
}
//...
}
//...
}
}
可以发现next方法是一个无限循环方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将其从单链表中移除。
3. Looper的工作原理
Looper在Android消息机制中扮演着消息循环的角色, 它会不停的从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。对于Looper我们需要分析下它的创建过程以及它是如何轮询消息的。
3.1. Looper的创建
Handler的工作需要Looper,如果当前线程没有Looper对象线程就会报错,那么如何为当前线程创建Looper呢?其实很简单,通过Looper.prepare()即可为当前线程创建一个Looper对象,接着通过Looper.loop()来开启消息循环,如下所示:
new Thread("Thread#1"){
@Override
public void run() {
super.run();
//创建Looper对象
Looper.prepare();
Handler handler = new Handler();
//开启消息轮询
Looper.loop();
}
}.start();
我们先来分析Looper的prepare方法,看看Looper对象是怎么创建以及存取的,代码如下:
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 @Nullable Looper myLooper() {
return sThreadLocal.get();
}
分析上面的prepare方法可以知道,Looper对象是通过ThreadLocal实现在线程中存取的,换句话说就是Looper的作用域是线程,每个线程都有各自的Looper对象。并且当前线程创建Looper后,如果再次调用perpare方法,会抛出RuntimeException异常,所以每个线程都只有一个Looper对象。
下面我们再看下Looper的构造方法,在构造方法中它会创建一个MessageQueue(消息队列),然后将当前线程的对象保存起来,如下所示:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
3.2. loop方法分析
Looper最重要的一个方法是loop方法,只有调用了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.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// 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 = me.mQueue.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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
msg.target.getTraceName(msg);
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
Looper的loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null。当Looper的quit方法被调用时,Looper就会调用MessageQueue的quit或者quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态时,它的next方法就会返回null。也就是说,Looper必须退出,否则loop方法就会无限循环下去。loop方法会调用MessageQueue的next方法来获取新消息,而next是一个阻塞操作,当没有消息时,next方法会一直阻塞在那里,这也导致loop方法一直阻塞在那里。
如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage方法来处理了。但是这里不同的是,Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就功地将代码逻辑切换到指定线程中去执行了。
4. Handler的工作原理
Handler的主要工作是发送和接收消息。 Handler可以通过post的一系列方法或send的一系列方法来发送消息,当然最终都是通过sendMessageAtTime方法来发送消息的,在sendMessageAtTime方法中,通过调用MessaageQueue的enqueueMessage将消息插入到消息队列中。Handler接收消息是在handleMessage方法中,Looper查询到新消息后,最终会调用Handler的handleMessage方法,这样消息最终又回调到了handleMessage放中。
4.1. Handler发送消息
Handler提供了post的一系列方法或send的一系列方法来发送消息,如下所示:
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postDelayed(Runnable r, int what, long delayMillis) {
return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
}
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,Handler提供的post系列方法和send系列方法最终都是调用了sendMessageAtTime方法,而sendMessageAtTime方法最终调用了MessageQueue的enqueueMessage方法向消息队列中插入了一条消息。
至此,Handler发送消息过程就完成了。
4.2. Handler处理消息
在分析Looper的时候,我们说到Looper的loop方法中,如果有新消息会交给Handler处理,即Handler的dispatchMessage方法会被调用,dispatchMessage源码如下:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在dispatchMessage方法中,首先检查Message的callback是否为null,不为null就通过handleCallback来处理消息。Message的callbakc是一个Runnable对象,实际就是Handler的post方法所传递的Runnable参数。
//callback就是post方法所传递的Runnable对象
//public final boolean post(@NonNull Runnable r) {
// return sendMessageDelayed(getPostMessage(r), 0);
//}
private static void handleCallback(Message message) {
message.callback.run();
}
```
- 其次,检查mCallback是否为null,不为null就调用mCall的handleMessage方法来处理消息,Callback是个接口,它的定义如下:
- ```
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
通过Callback可以采用如下方式来创建Handler对象,Handler handler = new Handler(callback)。那么Callback的意义是什么呢?源码里面的注释已经做了说明:可以用来创建一个Handler的实例但并不需要派生Handler的子类。
最后,调用Handler的handleMessage方法来处理消息。
网友评论