Android的消息机制主要是指Handler的运行机制,Handler的主要作用就是将一个任务切换到某个指定的线程去执行。Handler的运行需要底层的MessageQueue和Looper的支撑。Handler内部获取当前线程的Looper时,用到了ThreadLocal。
MessageQueue:单链表结构的消息队列,便于消息的插入和读取(本身会伴随着删除)操作。只用于存储消息,不负责处理。
类说明:Low-level class holding the list of messages to be dispatched by a {@link Looper}. Messages are not added directly to a MessageQueue, but rather through {@link Handler} objects associated with the Looper. You can retrieve the MessageQueue for the current thread with {@link Looper#myQueue() Looper.myQueue()}
//主要是单链表的插入操作。
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.
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;
}
next() : 一个无限循环的方法,如果消息队列中没有消息,则next方法会一直阻塞在这里;当有新消息到来时,next方法会返回这条消息并将其从单链表中移除。
Looper:以无限循环的方式去查找是否有新消息,如果有的话,交给Handler处理,否则就一直阻塞在那里。
类说明:Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call {@link #prepare} in the thread that is to run the loop, and then {@link #loop} to have it process messages until the loop is stopped.
Most interaction with a message loop is through the {@link Handler} class.
This class contains the code required to set up and manage an event loop based on MessageQueue. APIs that affect the state of the queue should be defined on MessageQueue or Handler rather than on Looper itself. For example, idle handlers and sync barriers are defined on the queue whereas preparing the thread, looping, and quitting are defined on the looper.
//私有的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //创建一个消息队列
mThread = Thread.currentThread(); //将当前线程的对象保存起来
}
//为当前线程创建一个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));
}
loop() : 开启消息循环,调用该方法后,消息循环系统才会真正的起作用。
该方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null。也就是说,Looper必须退出,否则loop方法会无限循环下去。
如果MessageQueue的next返回了新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage方法来处理了,在创建Handler时所使用的Looper中执行,这样就成功将代码逻辑切换到指定线程中执行了。
Looper也是可以退出的,quit方法会直接退出;quitSafely只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全退出。这两个方法其实都是调用MessageQueue的quit(boolean safely)方法,当消息队列标记为退出状态,它的next方法就会返回null。Looper退出后,通过Handler发送的消息会失败,send方法的返回值是false。在子线程中,如果手动为其创建了Looper,那么在所有的事情完成后应该调用quit方法来终止消息循环,否则这个子线程会一直处于等待的状态,而如果退出Looper,这个线程就会立刻终止。
ThreadLocal:并不是线程,它可以在不同线程中互不干扰的存储并提供数据,常用于以线程为作用域并且不同的线程有不同的数据副本的情况。
类说明:This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist)
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map !=null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e !=null)
return (T)e.value;
}
return setInitialValue(); //返回null,同时执行set(null)的代码
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; //Thread类的内部有一个成员专门用于存储线程的ThreadLocal数据
}
从ThreadLocal的set和get方法可以看出,它们所操作的对象都是当前线程的threadLocals对象,因此在不同的线程中访问同一个ThreadLocal的set和get方法,它们对Threadl所做的读写操作仅限于各自线程的内部,所以ThreadLocal可以在多个线程中互不干扰的存储和修改数据。
Handler:主要是消息的发送和接收处理过程。
类说明:A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.
When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling he same post or sendMessagemethods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.
//常用构造函数之一,所有的构造函数都会初始化Looper、MessageQueue等
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;
}
//Handler发送消息的过程
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);
}
//Handler处理消息的过程
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
//mCallback的意义:用来创建一个Handler的实例但是不需要派生Handler的子类
//日常开发中,常见的是派生一个Handler的子类并重写handleMessage方法来处理具体的消息
//Callback提供了另一种使用方式
if (mCallback !=null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Handler创建的时候会采用当前线程的Looper来构造消息循环系统,如果当前线程没有Looper,那么就会报错。所以,要么给当前线程创建Looper,要么在一个有Looper的线程中创建Handler。
Handler发送消息的过程仅仅是向消息队列中插入一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper接收到消息后就将消息交给Handler处理了,即Handler的dispatchMessage方法会被调用,这时Handler就进入了处理消息的阶段。
注意:1. 线程是默认没有Looper的,如果需要使用Handler就必须为线程创建Looper。UI线程,即ActivityThread,它在被创建是就会初始化Looper,所以在主线程中可以直接使用Handler。
- 在子线程中,如果手动为其创建了Looper,那么在所有的事情完成后应该调用quit方法来终止消息循环,否则这个子线程会一直处于等待的状态,而如果退出Looper,这个线程就会立刻终止。
3. 退出Looper后,该线程中赋值的handler发消息会有IllegalStateException异常,msg.target +" sending message to a Handler on a dead thread"。
Handler常用于UI更新, Android系统为什么不允许在子线程中更新UI?
答:因为Android的UI控件并不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。那为什么不对UI控件的访问加上锁机制呢?缺点有两个:一是加上锁机制会让UI访问的逻辑变得更复杂;二是锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。鉴于这些,最简单高效的方法就是采用单线程模型来处理UI操作。