Android的消息机制

Android的消息机制主要是Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程。

MessageQueue是消息队列,主要负责存储消息,以队列的形式对外提供插入和删除操作,但他真正的实现是采用单链表的数据结构来存储消息的。
Looper消息循环,MessageQueue只是负责存储消息,并不会处理消息,Looper会以无限循环的形式去查看MessageQueue中是否有新的消息,如果有新的消息的话,就将消息取出来处理,如果没有消息的话,就一直阻塞在那里。

创建Handler的时候会通过ThreadLocal获取当前线程的Looper。线程默认是没有Looper的,如果想要在线程中使用Handler,就必须为线程创建Looper,我们之所能够在主线程中使用Handler,是因为ActivityThread在创建的时候初始化了Looper。

Handler的主要作用就是将一个任务切换到某一个指定的线程中去运行。

Android之所以提供Handler,就是为了解决在子线程中无法访问UI的矛盾。

Android系统之所以规定不能在子线程中访问UI,是因为Android的UI控件并不是线程安全的,如果并发的访问会带来问题。

Android系统之所以不对访问UI的操作加锁,主要是由于两个原因:一个是加锁会让访问UI的逻辑变得复杂,另一个就是因为加锁可能会阻塞某一些线程,会降低访问的效率。因此为了解决这个问题,最简单高效的就是通过单线程模型来处理UI操作。

ThreadLocal

作用:线程内部共享变量,线程间变量独立。

应用场景:

  • 某些数据的作用区域是整个线程,而且在不同线程间具有不同的副本,这时候可以使用ThreadLocal;
  • 复杂逻辑下对象的传递,比如监听器的传递。在某些情况下线程内的任务比较复杂,可能是函数调用栈比较深,也有可能是代码入口多样性,而这时候又要求监听器贯穿函数的始终,这时候可以使用ThreadLocal来解决。如果不使用ThreadLocal的话,有两种方案解决,一种是将监听器作为参数进行传递,当函数调用栈比较深的时候,这种方法是不可取的,另一种是将监听器设置成静态变量供线程访问,但是这种方式的扩展性不好,十个线程就需要设置十个静态变量。

ThreadLocal的get和set方法,首先会从当前线程中取出一个Values对象,然后从Values对象中取出一个数组,数据的存取都是在这个数组上进行的,所以在不同的线程中使用ThreadLocal的get方法的时候可以得到不同的值。

源码解析:

Java中的ThreadLocal源码解析(上)

Java中的ThreadLocal源码解析(下)

Android中的ThreadLocal源码解析

ThreadLocal源码理解

MessageQueue

消息队列,负责存储消息,主要有插入和删除消息两种操作,实际是用单链表来实现的,因为单链表在插入和删除操作上具有优势。

插入消息:enqueueMessage方法,主要是单链表的插入操作。
读取并从链表删除消息:next方法,next方法是一个无限循环方法,如果没有消息会一直阻塞在那里,当有消息到来时候,会返回消息,并将消息从单链表中移除。

Looper

消息循环,Looper会不停的去查看MessageQueue中是否有新的消息,如果有新的消息的话,就将消息取出来处理,如果没有消息的话,就一直阻塞在那里。

Looper中的字段:

1
2
3
4
5
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class

final MessageQueue mQueue;
final Thread mThread;

sThreadLocal,负责存储每个线程的Looper;
sMainLooper,主线程的Looper;
mQueue,每个Looper里面都有一个消息队列;
Thread,Looper所在的线程;

构造方法:

1
2
3
4
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

构造方法十私有方法,要想为当前线程创建一个Looper,需要调用prepare方法:

1
2
3
4
5
6
 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,如果有的话,就抛出异常,所以在一个线程里只能调用一次prepare方法;如果当前线程没有Looper,就创建Looper,并set进sThreadLocal里面。

myLooper()方法:

1
2
3
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

获取当前线程的Looper。

prepareMainLooper()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

开发者无需调用这个方法,这个方法会在ActivityThread的main方法中被调用,为主线程创建Looper,这也是为什么我们可以直接在主线程中使用Handler的原因。

getMainLooper():

1
2
3
4
5
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}

获取主线程的Looper。

myQueue()方法:

1
2
3
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}

得到当前线程的Looper的MessageQueue。

isCurrentThread()方法:

1
2
3
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}

判断当前线程是否是Looper所在的线程。

loop()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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;

// 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;
}

// 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);
}

final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}

// 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,如果当前线程的Looper为空,则报错,所以loop方法必须在prepare方法之后调用。
取得当前线程的Looper之后,再从Looper中取出Looper的MessageQueue,接着就是一个死循环,跳出这个死循环的唯一方法就是next方法返回null,当使用完后,loop方法必须退出,否则Looper所在的线程会一直阻塞在那里,我们可以调用Looper的quit和quitSafely方法退出loop方法,当调用quit和quitSafely方法的时候,会调用MessageQueue的quit方法将消息队列标记为退出状态,当消息队列被标记为退出状态的时候,next方法会返回null。
next方法会一直阻塞在那里,直到有新的消息到来,当有新的消息的时候,next会将消息取出并处理, msg.target.dispatchMessage(msg),msg.target就是发送消息的handler对象,这样消息最终交给了hanler的dispatchMessage方法来处理,只不过这个方法是在Looper所在的线程中调用的,也就是将任务切换到了Looper所在的线程执行。

quit方法:

1
2
3
public void quit() {
mQueue.quit(false);
}

直接退出Looper,不管MessageQueue中还有没有待处理的消息。

quitSafely()方法:

1
2
3
public void quitSafely() {
mQueue.quit(true);
}

处理完消息队列中已经有的消息后再退出。

如果手动的为某个线程创建了Looper,那么当事情做完之后,应该调用quit方法来终止循环,否则线程会一直的处于等待状态。

Handler

字段:

1
2
3
4
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;

mLooper,当前线程的Looper;
mQueue,当前线程的Looper的MessageQueue;
mCallback,一个Callback接口;

接口:

1
2
3
public interface Callback {
public boolean handleMessage(Message msg);
}

构造方法

A构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

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为空的话,就会报错,所以在创建Handler之前,需要调用Looper.prepare方法。

B构造方法:

1
2
3
public Handler() {
this(null, false);
}

调用了上面的A构造方法。

C构造方法:

1
2
3
public Handler(Callback callback) {
this(callback, false);
}

调用了第一个构造方法,通过这个构造方法,可以不用派生Handler的子类,就可以创建一个Handler。

D构造方法:

1
2
3
4
5
6
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

E构造方法:

1
2
3
public Handler(Looper looper) {
this(looper, null, false);
}

调用了D构造方法,通过这个构造方法,可以为Handler重新指定一个Looper。

F构造方法:

1
2
3
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}

handleMessage方法:

1
2
3
4
5
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

一般我们创建Handler的实例的时候,都需要重写这个方法来处理消息。

dispatchMessage方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

通过前面对Looper的loop方法的代码分析,我们可以知道Handler所发送的消息,最终被Handler的dispatchMessage方法所处理。
Handler处理消息的流程如下所示:
此处输入图片的描述

发送消息的send系列方法:

sendMessage方法:

1
2
3
4
 public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}

sendMessageDelayed方法:

1
2
3
4
5
6
7
 public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

sendMessageAtTime方法:

1
2
3
4
5
6
7
8
9
10
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);
}

enqueueMessage方法:

1
2
3
4
5
6
7
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

sendMessageAtFrontOfQueue方法:

1
2
3
4
5
6
7
8
9
10
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}

MessageQueue中是按照uptimeMillis排序的,而sendMessageAtFrontOfQueue方法中将它设为0,所以sendMessageAtFrontOfQueue方法传递的Message将在Looper的loop方法循环的下一次循环中立即执行,sendMessageAtFrontOfQueue方法只会在某些特殊的情况下会使用,因为这个方法可能会带来一些问题。

send方法最后就是调用了MessageQueue的enqueueMessage方法将消息插入到消息队列里面,在插入消息之前,会将当前的Handler赋值给Message的target字段,通过前面对Looper的loop代码的分析可以知道,在Looper的loop方法中会执行msg.target.dispatchMessage(msg)方法。

发送消息的post系列方法

post方法

1
2
3
4
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}

post方法调用了前面的sendMessageDelayed方法。

getPostMessage方法:

1
2
3
4
5
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

getPostMessage方法就是创建一个Messsage,并将Runnable赋值给Messagede的callback字段,最后将Message返回。

postAtTime方法:

1
2
3
4
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

调用了前面的sendMessageAtTime方法。

postAtTime方法:

1
2
3
4
 public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

调用了sendMessageAtTime方法。

getPostMessage(Runnable r, Object token) 方法:

1
2
3
4
5
6
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}

postDelayed方法

1
2
3
4
 public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}

调用了前面的sendMessageDelayed方法。

postAtFrontOfQueue方法

1
2
3
4
 public final boolean postAtFrontOfQueue(Runnable r)
{
return sendMessageAtFrontOfQueue(getPostMessage(r));
}

调用了前面的sendMessageAtFrontOfQueue方法。

通过源码分析可以知道,post方法都是通过调用send方法来实现的。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器