Android 基础 十一 Android的消息机制

  Handler是Android消息机制的上层接口,这使得在开发应用过程中我们只需要和Handler交互即可。Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。当耗时操作完成以后可能需要在UI上做一些改变,由于Android开发规范的限制,我们不能在子线程中访问UI空间,否则就会触发程序异常,这个时候通过Handler就可以将更新UI的操作切换到主线程中执行。
  Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑。MessageQueue的中文翻译是消息队列,顾名思义,它的内部存储了一组消息,以队列的形式对外提供插入和删除的工作。虽然叫消息队列,但是它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。Looper的中文翻译为循环,在这里可以理解为消息循环。由于MessageQueue只是一个消息的存储单元,它不能去处理消息,而Looper就填补了这个功能,Looper会以无限循环的方式去查找是否有新消息,如果有的话就处理消息,否则就一直等待着。Looper还有一个特殊的概念,那就是ThreadLocal,ThreadLocal可以在不同的线程中互不干扰地存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。当然需要注意的是,线程默认是没有Looper的,如果需要使用Handler就必须为线程创建Looper。我们经常提到的主线程,也叫UI线程,它就是ActivityThread,ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。 

一 Handler使用

  前面提到,Android的消息机制主要时指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程,这三者实际上是一个整体,只不过我们在开发过程中比较多的接触到Hanlder而已。Handler的主要作用是将一个任务切换到某个指定的线程中去执行,那么Android为什么提供这个功能呢?这是因为Android规定访问UI必须在主线程中进行,如果子线程访问UI,那么程序或抛出异常(android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.)。frameworks/base/core/java/android/view/ViewRootImpl.java的checkThread方法来完成的,如下所示。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}

  针对checkThread方法中抛出的异常信息,相信大家在开发中都曾经遇到过。由于这一点的限制,导致必须在主线程中访问UI,但是Android又建议不要在主线程中进行耗时操作,否则会导致程序无法响应即ANR。考虑一种情况,假如我们需要从服务端拉取一些信息并将其显示在UI上,这个时候必须在子线程中进行拉取工作,拉取完毕后又不能在子线程中直接访问UI,如果没有Handler,那么我们的确没办法将访问UI的工作切换到主线程中去执行。因此系统之所以提供Handler,主要原因是为了解决在子线程中无法访问UI的矛盾。

  这里再延伸一点,系统为什么不允许在子线程中访问UI呢?这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI处于不可预期的状态,那为什么系统不对UI控件访问加上锁机制呢?缺点有两个:首先上锁机制会让UI访问的逻辑变得复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。鉴于这两个缺点,最简单高效的方法就是采用单线程模型来处理UI操作,对于开发者来说也不是很麻烦,只是需要通过Handler切换一下UI访问的执行线程即可。 

1.1 Handler的使用方法

1.1.1 常见方法

sendEmptyMessage:
mHandler.sendEmptyMessage(1001);
mHandler.sendEmptyMessageAtTime(1002,SystemClock.uptimeMillis()+1000);
mHandler.sendEmptyMessageDelayed(1003,3000);
取消:mHandler.removeMessages(1003);
sendMessage:
Message message2 = Message.obtain();
mHandler.sendMessage(message1);
mHandler.sendMessageAtTime(message2,SystemClock.uptimeMillis()+1000);
mHandler.sendMessageDelayed(message3,3000);
取消:mHandler.removeMessages(1003);
post:
mHandler.post(runnable);
mHandler.postDelayed(runnable,3000);
取消:mHandler.removeCallbacks(runnable);
sendToTarget
Message message = mHandler.obtainMessage();
message.sendToTarget();
public void sendToTarget() {
target.sendMessage(this);
}

在Message的官方文档中,有这样一句话:

     While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.
    意思是说,虽然Message的构造方法是公共的(可以通过New操作创建一个Message对象),但获取实例最好的方式还是Message.obtain()或者 Handler.obtainMessage() ,这两种方法提供的对象是消息池中那些已经创建但不再使用的对象。节约了内存资源。

1.1.2 sendMessage源码分析

frameworks/base/core/java/android/os/Handler.java
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) {
//异常处理
}
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()方法首先设置了这个消息的目标处理对象target,即这个消息最终由谁来处理,这里赋值为this,表示这个消息最终由目前发送的Handle来处理,接着enqueueMessage方法调用MessageQueue中enqueueMessage方法来把这个消息加入到应用程序的消息队列中去,queue是一个MessageQueue对象

frameworks/base/core/java/android/os/MessageQueue.java
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;
}

  Handler发送消息最终都会调用到sendMessageAtTime方法,在这个方法里面就做了一件事,将消息放入一个消息队列中。如果新消息首次加入队列,或者新消息延迟时间要小于队列首个消息延迟时间,就将新消息放在队列首部。如果队列不为空,新消息延迟时间并不是最短,就将新消息插入到队列的某个位置。

  总之,新消息插入队列后,消息队列是按延迟时间长短排序的一个有序队列,延迟时间越短,越靠近队列首部。

1.2 Handler简单实例

  讲解Handler工作原理之前可以将下面代码运行一下,
public class HandlerActivity extends Activity implements View.OnClickListener {
private Button btn_main_to_sub;
private Button btn_sub_to_main;
Handler mMainHandler;
Handler mSubHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
mMainHandler = new MainHandler();
new MainToSubThread().start();
initViews();
}
private void initViews() {
btn_main_to_sub = findViewById(R.id.main_to_sub);
btn_sub_to_main = findViewById(R.id.sub_to_main);
btn_sub_to_main.setOnClickListener(this);
btn_main_to_sub.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.main_to_sub:
Log.d("panzqww", "onClick --- MainThread to SubThread");
Message msg = mSubHandler.obtainMessage();
msg.obj = "从主线发送给子线程的消息 555";
mSubHandler.sendMessage(msg);
break;
case R.id.sub_to_main:
Log.d("panzqww", "onClick --- SubThread to MainThread");
new SubToMainThread().start();
break;
}
}
class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String msg_obj = (String) msg.obj;
longRunningMethod();//主线程运行耗时操作会出现ANR异常
Log.d("panzqww", "我是主线程 ,接收到子线程的消息: " + msg_obj);
}
}
class SubToMainThread extends Thread {
@Override
public void run() {
super.run();
Message msg = mMainHandler.obtainMessage();
msg.obj = "从子线发送给主线程的消息 666";
mMainHandler.sendMessage(msg);
}
}
class MainToSubThread extends Thread {
@Override
public void run() {
super.run();
Looper.prepare();
mSubHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String msg_obj = (String) msg.obj;
Log.d("panzqww", "我是子线程,接收到主线程的消息:" + msg_obj);
longRunningMethod();//子线程执行耗时操作不会出现ANR异常
}
};
Looper.loop();
}
}
private void longRunningMethod() {
long i = 0;
long sum = 0;
while (i < 10000) {
sum += i;
SystemClock.sleep(1);
i++;
}
Log.d("panzqww", " 耗时操作结果 sum = " + sum);
}
}

上述代码想要阐述的是:

  • 1.如何在Thread中定义一个Handler
  需要用到Looper.prepare()、Looper.loop()方法,后面会讲到。
  • 2.在Thread定义的Handler跟在主线程中的Handler有什么区别
  在子线程中定义Handler,优点是不会有堵塞,减少了对性能的消耗,缺点是不能同时进行多任务的处理,需要等待进行处理。处理效率较低。而主线的中的Handler如果处理耗时操作,系统便会阻塞,此时操作系统便会出现ANR异常。

二、Android的消息机制分析

  在上节中对Android的消息机制已经做了一个概括性的描述。本节将对Android消息机制的实现原理做一个全面的分析。由于Android的消息机制实际上是Handler的运行机制,Handler的消息处理主要有五个部分组成:Message,Handler,MessageQueue,Looper和ThreadLocal。首先简要的了解这些对象的概念
  • Message:Message是在线程之间传递的消息,它可以在内部携带少量的数据,用于线程之间交换数据。Message有四个常用的字段,what字段,arg1字段,arg2字段,obj字段。what,arg1,arg2可以携带整型数据,obj可以携带object对象。
  • Handler:它主要用于发送和处理消息,发送消息一般使用sendMessage()方法,还有其他的一系列sendXXX的方法,但最终都是调用了sendMessageAtTime方法。而发出的消息经过一系列的辗转处理后,最终会传递到Handler的handleMessage方法中。
  • MessageQueue:MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息,这部分的消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
  • Looper:每个线程通过Handler发送的消息都保存在MessageQueue中,Looper通过调用loop()的方法,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。
  • ThreadLocal:MessageQueue对象和Looper对象在每个线程中都只会有一个对象,怎么能保证它这个对象,就通过ThreadLocal来保存。ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储到数据,对于其他线程来说则无法获取到数据。
  了解了这些基本概念后,我们深入源码来了解Handler的工作机制。

2.1 ThreadLocal的工作原理

2.1.1 ThreadLocal单独介绍

libcore/luni/src/main/java/java/lang/ThreadLocal.java
/**
* Implements a thread-local storage, that is, a variable for which each thread
* has its own value. All threads share the same {@code ThreadLocal} object,
* but each sees a different value when accessing it, and changes made by one
* thread do not affect the other threads. The implementation supports
* {@code null} values.
*
* @see java.lang.Thread
* @author Bob Lee
*/

  以上是源码(libcore/luni/src/main/java/java/lang/ThreadLocal.java)中对ThreadLocal的解释,意思是“实现线程本地存储,即每个线程都有自己的值。所有线程都共享同一个{@code ThreadLocal}对象,但是在访问时,每个线程都看到不同的值,一个线程所做的更改不会影响其他线程。该实现支持{@code null}值”。从这里我们可以看出,ThreadLocal相当于一个容器,存储着每个线程的数据,且所有线程都共享这一个ThreadLocal变量,但每个线程访问该对象时竟然会获得的数据都不同,而且就算修改这些数据也不会影响到其他线程,即所有线程互不干扰。因为这个特性,ThreadLocal在Java中也常用于处理多线程,也就是说,ThreadLocal和线程同步机制一样,都是为了解决多线程访问同一变量造成的访问冲突问题,不同的的是:同步机制是通过锁机制牺牲时间来达到多线程安全访问的目的;ThreadLocal为每个线程提供独立的变量副本,牺牲空间保证每个线程访问相同变量时可以得到自己专属的变量,达到多线程安全访问的目的。

  ThreadLocal是一个线程内部的数据存储类,通过它在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。在日常开发中用到ThreadLocal的地方较少,但是在某些特殊的场景下,通过ThreadLocal可以轻松地实现一些看起来很复杂的功能,这一点在Android源码中也有所体现,比如Looper、ActivityThread以及AMS中都用到了ThreadLocal。一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。比如对于Handler来说,它需要获取当前线程的Looper,这个时候需要通过ThreadLocal可以轻松实现Looper在线程中的存取。如果不采用ThreadLocal,那么系统就必须提供一个全局的哈希表供Handler查找指定线程的Looper,这样一来就必须提供一个类似于LooperManager的类了,但是系统并没有这么做而是选择了ThreadLocal,这就是ThreadLocal的好处。 
  ThreadLocal另一个使用场景是复杂逻辑下的对象传递,比如监听器的传递,有些时候一个线程中的任务过于复杂,这可能表现为函数调用栈比较深以及代码入口的多样性,在这种情况下,我们又需要监听能够贯穿整个线程的执行过程,这个时候可以怎么做呢?其实这时候就可以采用ThreadLocal让监听器作为线程内的全局对象而存在,在线程内部只需要通过get方法就可以获取到监听器。如果不采用ThreadLocal,那么我们能想到的可能是如下两种方法:第一种方法是将监听器通过参数的形式在函数调用栈中进行传递,第二种方法就是将监听器作为静态变量在线程中访问。上述这两种方法都是有局限性的。第一种方法的问题是当函数调用栈很深的时候,通过函数参数来传递监听器对象这几乎是不可接受的,这会让程序的设计看起来很糟糕。第二种方法是可以接受的,但是这种状态是不具有可扩展性的,比如同时有两个线程在执行,那么就需要通过两个静态的监听对象,如果有10个线程在并发执行呢?提供10个静态的监听器对象?这显然是不可思议的,而采用ThreadLocal,每个监听器对象都在自己的线程内部存储,根本就不会有方法2的这种问题。
  介绍了那么多的ThreadLoacl的知识,可能还是有点抽象,下面通过实际的例子来演示ThreadLoacl的真正含义。首先定义一个ThreadLoacl对象,这里选择Boolean类型的,如下所示。   
 private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<Boolean>();
    然后分别在主线程、子线程1和子线程2中设置和访问它的值,代码如下所示。         
mBooleanThreadLocal.set(true);
Log.d(TAG, "Thread#main mBooleanThreadLocal=" + mBooleanThreadLocal.get());
new Thread("Thread#1"){
@Override
public void run() {
mBooleanThreadLocal.set(false);
Log.d(TAG, "Thread#1 mBooleanThreadLocal=" + mBooleanThreadLocal.get());
}
}.start(); new Thread("Thread#2"){
@Override
public void run() {
Log.d(TAG, "Thread#2 mBooleanThreadLocal=" + mBooleanThreadLocal.get());
}
}.start();
  在上面的代码中,在主线程中设置mBooleanThreadLocal的值为true,在子线程中设置mBooleanThreadLocal的值为false,在子线程2中不设置mBooleanThreadLocal的值。然后分别在3个线程中通过get方法获取mBooleanThreadLocal的值,根据前面对ThreadLocal的描述,这个时候,主线程中应该时true。子线程中应该时false,而子线程2中由于没有设置值,所以应该时null。安装并运行程序,日志如下所示。
D/panzqww: (Thread#main)mBooleanThreadLocal=true
D/panzqww: (Thread#)mBooleanThreadLocal=false
D/panzqww: (Thread#)mBooleanThreadLocal=null
  从上面日志可以看出,虽然在不同线程中访问的是同一个ThreadLocal对象,但是它们通过ThreadLocal获取到的值确是不一样的,这就是ThreadLocal的奇妙之处。结合这个例子然后再看一遍前面对ThreadLocal的两个使用场景的理论分析,我们应该就能比较好地理解ThreadLocal的使用方法了。ThreadLocal之所以有这么奇妙的效果,是因为不同线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各种线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找对应的value值。很显然,不同线程中的数组是不同的,这就是为什么通过ThreadLocal可以在不同的线程中维护一套数据的副本彼此互不干扰的。

2.1.2 ThreadLocal内部实现

  在Handler机制中,我们需要新建一个线程,而在每个Thread中,都会包含一个ThreadLocal.Values 类型的localValues变量,表示该线程映射到ThreadLocal中的值。
libcore/luni/src/main/java/java/lang/ThreadLocal.java
ThreadLocal.Values
/** Size must always be a power of 2. */
private static final int INITIAL_SIZE = 16;
/** Placeholder for deleted entries. */
private static final Object TOMBSTONE = new Object();
/**
* Map entries. Contains alternating keys (ThreadLocal) and values.
* The length is always a power of 2.
*/
private Object[] table;
/** Used to turn hashes into indices. */
private int mask;
/** Number of live entries. */
private int size; /** Number of tombstones. */
private int tombstones; /** Maximum number of live entries and tombstones. */
private int maximumLoad; /** Points to the next cell to clean up. */
private int clean; Values() {
initializeTable(INITIAL_SIZE);
this.size = 0;
this.tombstones = 0;
} private void initializeTable(int capacity) {
this.table = new Object[capacity * 2];
this.mask = table.length - 1;
this.clean = 0;
this.maximumLoad = capacity * 2 / 3; // 2/3
}
  这里主要是对几个变量进行初始化初始化。包括用于存储线程本身一些数据的Object数组table,该数组也存储了线程间共享的ThreadLocal对象和自身相关的Looper 对象,还有用于将哈希值转为指数的掩码。
记住该Values类后我们来看ThreadLocal是如何在消息机制中发挥作用的。
  通常我们都是通过Looper.prepare()来构建looper实例的,但是该方法除了实例化一个looper并创建一个MessageQueue、指定其mThread为当前线程外,就是将该Looper对象存到ThreadLocal中。
frameworks/base/core/java/android/os/Looper.java
public static void prepare() {
prepare(true);
}
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));
}
  对ThreadLocal的使用方法和工作过程做了介绍后,下面分析ThreadLocal的内部实现,ThreadLocal是一个泛型类,它的定义为public class ThreadLocal<T>,只要弄清楚ThreadLocal的get和set方法就可以明白它的工作原理。
  首先看ThreadLocal的set方法,如下所示。
libcore/luni/src/main/java/java/lang/ThreadLocal.java
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
} Values values(Thread current) {
return current.localValues;
} Values initializeValues(Thread current) {
return current.localValues = new Values();
}
  这里 values(currentThread) 方法返回的就是我们前面提到的ThreadLocal.Values对象,也就是Thread类中的localValue值,如果是null就进行初始化,初始化执行的就是我们前面提到的构造方法。初始化成功后便将该ThreadLocal对象和该looper对象(即参数value)存储到该values对象中,具体实现如下:
libcore/luni/src/main/java/java/lang/ThreadLocal.java
void put(ThreadLocal<?> key, Object value) {
// Cleans up after garbage-collected thread locals.
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
private int next(int index) {
return (index + 2) & mask;
}
  从上面代码可以看到,该方法其实就是把传进来的ThreadLocal对象和looper对象存到它的table数组中,可以看到,这里通过计算该threadLocal的哈希值key.hash与掩码值mask 的与运算结果来判断table中是否存在该threadLocal对象的引用reference ,若存在则将该looper对象放在该引用在table数组中的位置的后一个位置,即table[index + 1];若不存在则将该threadLocal对象的引用存到table数组的[index]位置上,而looper对象同样存在其后一个位置。也就是说,looper的值都会存在threadLocal引用的后一个位置。
  然后就应该构造Handler获取looper了。在创建handler时,若参数中没有指定looper,会通过mLooper = Looper.myLooper(); 为自身指定一个looper对象。而myLooper实际上就是返回threadLocal.get()而已。
frameworks/base/core/java/android/os/Looper.java
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
  那么具体是如何获得当前线程相关的looper的呢?
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
Values values(Thread current) {
return current.localValues;
}
Values initializeValues(Thread current) {
return current.localValues = new Values();
}

  可以看到,这里依然是通过当前线程自身的localValues这个值内部的table数组来获得相关联的looper对象,如果是null,则重新初始化该Values对象,如果不为null则返回table数组中该ThreadLocal对象的引用所在位置的下一个值,也就是我们前面set进去looper。

  综上,我们可以知道,在handler机制中,之所以每个线程能够获得自身的looper,而不影响其他线程,是因为通过操作每个线程自身的localValues对象内部的table数组实现的,从而达到不同线程访问同一个threadLocal的get()和set()不会访问或修改到其他线程的数据。即对ThreadLocal所做的读写操作仅限于各自线程的内部,这就是为什么ThreadLocal可以在多个线程中互不干扰地存储和修改数据,理解ThreadLocal的实现方式有助于理解Looper的工作原理。

2.2 MessageQueue的工作原理

2.2.1 消息队列读取

  在创建Looper对象的时候,会同时创建一个消息队列MessageQueue,保存在Looper的成员变量mQueue中,后续消息就是存放在这个队列中去。消息队列在Android应用程序消息处理机制中最重要的组件,因此,我们看看它的创建过程,即它的构造函数的实现 frameworks/base/core/java/android/os/MessageQueue.java
public class MessageQueue { ...... private int mPtr; // used by native code private native static long nativeInit(); MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
} ...... }
  MessageQueue主要包含两个操作:插入和读取消息。读取本身会伴随着删除操作,插入和读取对象的方法分别为enqueueMessage和next,其中enqueueMessage的作用是往消息队列中插入一条消息,而next的作用是从消息队列中取出一条消息并从消息队列中移除。尽管MessageQueue叫消息队列,但它的内部实现并不是用的队列,实际上它是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。下面主要看一下它的enqueueMessage和next方法的实现,enqueueMessage的源码如下所示。
frameworks/base/core/java/android/os/MessageQueue.java    
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;
}
  enqueueMessage方法中if和else判断,if分支表示目前消息队列为空,这时应用程序主线程处于空闲状态,也就是说它在等待消息的发送,这时如果有消息就把消息放在消息队列的前面,接着唤醒应用程序的主线程。而else分支表示,消息队列不为空,还有消息在处理,这时候不要唤醒应用程序主线程。nativeWake方法是JNI层一个native方法:
frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
NativeMessageQueue类的wake函数,又通过调用C++层mLooper对象的wake()函数进一步操作
system/core/libutils/Looper.cpp
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
Looper.cpp里的wake()函数通过如下代码调用:
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
  write()函数,mWakeEventFd就发生了一个event事件,这时前面监听的epoll机制就起了作用,会唤醒应用程序主线程,于是应用程序主线程就会从前面的C++层的Looper类的polllner()函数一层层返回,最后返回到java层的MessageQueue类的next()方法中 。
  下面看一下next方法的实现,next的主要逻辑如下所示。
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();
}
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;
if (msg != null) {
if (now < msg.when) {
} 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 = 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;
}
}
  队列被激活之后,首先判断队首是不是消息屏障,如果是则跳过所有的同步消息,查找最先要处理的异步消息。如果第一个待处理的消息还没有到要处理的时机则设置激活等待时间;否则这个消息就是需要处理的消息,将该消息设置为 inuse,并将队列设置为非 blocked 状态,然后返回该消息。next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息到来时,next的方法会返回这条消息并将其从单链表中移除。
  其中nativePollOnce(ptr, nextPollTimeoutMillis)是一个native方法,是一个阻塞操作。其中nextPollTimeoutMillis代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。空闲后,往往会执行IdleHandler中的方法。当nativePollOnce()返回后,next()从mMessages中提取一个消息。nativePollOnce()在native做了大量的工作:
frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL; if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}

2.2.2 消息队列底层实现

  上面有讲到消息提取时使用到JNI的相关方法,这些JNI方法定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中。比如消息队列底的初始化工作都交给JNI方法nativeInit来实现了
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
...
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return ;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
  它主要就是在内部创建了一个Looper对象,注意,这个Looper对象是实现在JNI层的,它与Java层中的Looper是不一样的,不过它们是对应的,下面我们进一步分析消息循环的过程的时候,就会清楚地了解到它们之间的关系。
  android_os_MessageQueue_nativeInit函数创建了本地消息队列NativeMessageQueue对象之后,接着调用android_os_MessageQueue_setNativeMessageQueue函数来把这个消息队列对象保存在前面我们在Java层中创建的MessageQueue对象的mPtr成员变量里面:
frameworks/base/core/jni/android_os_MessageQueue.cpp
sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) {
jlong ptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
return reinterpret_cast<NativeMessageQueue*>(ptr);
}
  这里传进来的参数messageQueueObj即为我们前面在Java层创建的消息队列对象,而gMessageQueueClassInfo.mPtr即表示在Java类MessageQueue中,其成员变量mPtr的偏移量,通过这个偏移量,就可以把这个本地消息队列对象natvieMessageQueue保存在Java层创建的消息队列对象的mPtr成员变量中,这是为了后续我们调用Java层的消息队列对象的其它成员函数进入到JNI层时,能够方便地找回它在JNI层所对应的消息队列对象。 
  结合2.1,2.2节内容,总结一下如下图所示,下面的内容就开始讲解Looper的内容。
Android 基础 十一 Android的消息机制

2.3 Looper的工作原理

2.3.1 Looper上层实现

  2.2节中介绍了消息队列的主要实现,本节将分析Looper的具体实现。Looper在Android的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在哪里。首先看一下它的构造方法,在构造方法中它会创建一个MessageQueue即消息队列,然后将当前线程的对象保存起来,如下所示。frameworks/base/core/java/android/os/Looper.java
 public class Looper {
......
private static final ThreadLocal sThreadLocal = new ThreadLocal();
final MessageQueue mQueue;
......
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
/** 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.
* {@link #prepare()}
*/
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
......
}
  我们知道,Handler的工作需要Looper,没有Looper的线程就会报错,那么如何为一个线程创建Looper呢?其实在前面的实例中也讲到过,通过Looper.prepare()即可为当前线程创建一个Looper,接着通过Looper.loop()来开启消息循环,如下所示。
 new Thread("Thread#2"){
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();

  Looper除了prepare方法外,还提供了prepareMainLooper方法,这个方法主要时给主线程也就是ActivityThread创建Looper使用的,其本质也是通过prepare方法来实现的。由于主线程的Looper比较特殊,所以Looper提供了一个getMainLooper方法,即通过getMainLooper方法可以在任何地方获取到主线程的Looper。Looper也是可以退出的,Looper提供了quit和quitSafely来退出一个Looper,二者的区别是:quit会直接退出Looper,而quitSafely只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全地退出。Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send方法会返回false。在子线程中如果手动为其创建了Looper,那么在所有的事情完成以后应该调用quit方法来终止循环,否则这个子线程就会一直处于等待的状态,而如果退出了Looper以后,这个线程就会立刻终止,因此建议不需要的时候终止Looper。

  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.");
}
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
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.recycle();
}
}

  Looper的loop方法的工作过程比较好理解,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中执行的,这样就成功地将代码逻辑切换到指定地线程中去执行了。

2.3.2 Looper底层实现

  看看JNI层的Looper对象的创建过程,即看看它的构造函数是如何实现的,这个Looper类实现在system/core/libutils/Looper.cpp文件中:
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-), mEpollRebuildRequired(false),
mNextRequestSeq(), mResponseIndex(), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(, EFD_NONBLOCK);
LOG_ALWAYS_FATAL_IF(mWakeEventFd < , "Could not make wake event fd. errno=%d", errno); AutoMutex _l(mLock);
rebuildEpollLocked();
} Looper::~Looper() {
close(mWakeEventFd);
if (mEpollFd >= ) {
close(mEpollFd);
}
}
  这里用到Linux系统中的管道(pipe)知识,简单来说,管道就是一个文件。在管道的两端分别是两个打开文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,另一个是用来写的,一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容,当管道没有内容时,这个线程就会进入等待状态,而另外一个线程通过写文件描述符来向管道中写入内容,写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。这个等待和唤醒的操作要借助Linux系统中的epoll机制了。 Linux系统中的epoll机制为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。但是这里我们其实只需要监控的IO接口只有mWakeReadPipeFd一个,即前面我们所创建的管道的读端,为什么还需要用到epoll呢?有点用牛刀来杀鸡的味道。其实不然,这个Looper类是非常强大的,它除了监控内部所创建的管道接口之外,还提供了addFd接口供外界面调用,外界可以通过这个接口把自己想要监控的IO事件一并加入到这个Looper对象中去,当所有这些被监控的IO接口上面有事件发生时,就会唤醒相应的线程来处理,不过这里我们只关心刚才所创建的管道的IO事件的发生。
        要使用Linux系统的epoll机制,首先要通过epoll_create来创建一个epoll专用的文件描述符:
system/core/libutils/Looper.cpp
// Allocate the new epoll instance and register the wake pipe.
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < , "Could not create epoll instance. errno=%d", errno);
  传入的参数EPOLL_SIZE_HINT是在这个mEpollFd上能监控的最大文件描述符数。 接着还要通过epoll_ctl函数来告诉epoll要监控相应的文件描述符的什么事件:
system/core/libutils/Looper.cpp
struct epoll_event eventItem;
memset(& eventItem, , sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
LOG_ALWAYS_FATAL_IF(result != , "Could not add wake event fd to epoll instance. errno=%d",
errno);
  这里就是告诉mEpollFd,它要监控mWakeReadPipeFd文件描述符的EPOLLIN事件,即当管道中有内容可读时,就唤醒当前正在等待管道中的内容的线程。
  底层的这个Looper对象创建好了之后,就返回到JNI层的NativeMessageQueue的构造函数,最后就返回到Java层的消息队列MessageQueue的创建过程,这样,Java层的Looper对象就准备好了。有点复杂,我们先小结一下这一步都做了些什么事情:
       A. 在Java层,创建了一个Looper对象,这个Looper对象是用来进入消息循环的,它的内部有一个消息队列MessageQueue对象mQueue;
       B. 在JNI层,创建了一个NativeMessageQueue对象,这个NativeMessageQueue对象保存在Java层的消息队列对象mQueue的成员变量mPtr中;
       C. 在C++层,创建了一个Looper对象,保存在JNI层的NativeMessageQueue对象的成员变量mLooper中,这个对象的作用是,当Java层的消息队列中没有消息时,就使Android应用程序主线程进入等待状态,而当Java层的消息队列中来了新的消息后,就唤醒Android应用程序的主线程来处理这个消息。

2.4 Handler的工作原理

  在Hanlder类的构造函数中,主要就是初始成员变量mLooper和mQueue了。这里的myLooper是Looper类的静态成员函数,通过它来获得一个Looper对象,这个Looper对象就是前面我们在分析消息循环时,在ActivityThread类的main函数中通过Looper.prepareMainLooper函数创建的
frameworks/base/core/java/android/os/Handler.java
public class Handler {
......
public Handler() {
......
mLooper = Looper.myLooper();
......
mQueue = mLooper.mQueue;
......
} final MessageQueue mQueue;
final Looper mLooper;
......
}
  Handler的工作主要包含消息的发送和接收过程。消息的发送可以通过post的一些列方法以及send的一些列方法来实现,post的一些列方法最终是通过send的一些列方法来实现的。发送一条消息的典型过程如下所示。
frameworks/base/core/java/android/os/Handler.java
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);
}
  可以发现,Handler发送消息的过程仅仅是向消息队列中插入了一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息后就开始处理了,最终消息由Looper交由Handler处理,即Handler的dispatchMessage方法会被调用,这时Handler就进入了处理消息的阶段。dispatchMessage的实现如下所示。
frameworks/base/core/java/android/os/Handler.java
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();
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
  Handler处理消息的过程如下:
  首先,检查Message的callback是否为null,不为null就通过handleCallback来处理消息。Message的callback是一个Runnable对象,实际上是Handler的post方法所传递的Runnable参数。
  其次,检查mCallback是否为null,不为null就调用mCallback的handleMessage方法来处理消息。Callback是一个接口,它的定义如下:
/**
Callback interface you can use when instantiating a Handler to avoid having
to implement your own subclass of Handler.
@param msg A{@link android.os.Message Message} object
@return True if no further hadling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
  通过Callback可以采用如下方式来创建Handler对象:Handler handler= new Handler(callback)。那么Callback的意义是什么呢?源码里面的注释已经做了说明:可以用来创建一个Handler的实例并不需要派生Handler的子类。在日常开发中,创建Handler最常见的方式就是派生一个Handler的子类并重写其handleMessage方法来处理具体的消息,而Callback给我们提供了另外一种使用Handler的方式,当我们不想派生子类时,就可以通过Callback来实现。
  最后,调用Handler的handleMessage方法来处理消息。Handler处理消息的过程可以归纳为一个流程图,如图所示。 
Android 基础 十一 Android的消息机制
Handler还有一个特殊的构造方法,那就是通过一个特定的Looper来构造Handler,它的实现如下所示。通过这个构造方法可以实现一些特殊的功能。
 public Handler(Looper looper) {
this(looper, null, false);
}
  下面看一下Handler的一个默认构造方法public Handler(),这个构造方法会调用下面的构造方法。这个方法上面也就讲到,如果当前线程没有Looper的话,就会抛出"Can't create handler inside thread that has not called Looper.prepare()"这个异常,这也解释了在没有Looper的子线程中创建Handler会引发程序异常的原因。
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;
}

三、主线程的消息循环

  Android的主线程就是ActivityThread,主线程的入口方法为main,在main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环,这个过程如下所所示。frameworks/base/core/java/android/app/ActivityThread.java
public final class ActivityThread {
......
public static final void main(String[] args) {
.....
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
Looper.loop();
......
thread.detach();
......
}
}
  这个函数做了两件事情,一是在主线程中创建了一个ActivityThread实例,二是通过Looper类使主线程进入消息循环中
  主线程的消息循环开始了以后,ActivityThread还需要一个Handler来和消息队列进行交互,这个Handler就是ActivityThread.H,它内部定义了一组消息类型,它主要包含四大组件的启动和停止等过程,如下所示。
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
...
}
  ActivityThread通过ApplicationThread和AMS(ActivityManagerService)进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后会回调ApplicationThread中去执行,即切换到主线程去执行,这个过程就是主线程的消息循环模型。当AMS需要与应用程序进行交互时,如加载Activity和Service、处理广播等,会通过Binder进程间通信机制来知会应用程序,应用程序接收到这个请求时,它不是马上就处理这个请求,而是将这个请求封装成一个消息,然后把这个消息放在应用程序的消息队列中去,然后再通过消息循环来处理这个消息。这样做的好处就是消息的发送方只要把消息发送到应用程序的消息队列中去就行了,它可以马上返回去处理别的事情,而不需要等待消息的接收方去处理完这个消息才返回,这样就可以提高系统的并发性。
  ActivityManagerService通过调用ApplicationThread类的scheduleLaunchActivity函数通知应用程序,它可以加载应用程序的默认Activity了,这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
private final class ApplicationThread extends ApplicationThreadNative {
......
// we use token to identify this activity without having to send the
// activity itself back to the activity manager. (matters more with ipc)
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) {
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.activityInfo = info;
r.state = state;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
}
......
}
......
}
  这里把相关的参数都封装成一个ActivityClientRecord对象r,然后调用queueOrSendMessage函数来往应用程序的消息队列中加入一个新的消息(H.LAUNCH_ACTIVITY),这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
private final class ApplicationThread extends ApplicationThreadNative {
......
// if the thread hasn't started yet, we don't have the handler, so just
// save the messages until we're ready.
private final void queueOrSendMessage(int what, Object obj) {
queueOrSendMessage(what, obj, 0, 0);
}
......
private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
synchronized (this) {
......
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
mH.sendMessage(msg);
}
}
......
}
......
}
  在queueOrSendMessage函数中,又进一步把上面传进来的参数封装成一个Message对象msg,然后通过mH.sendMessage函数把这个消息对象msg加入到应用程序的消息队列中去。这里的mH是ActivityThread类的成员变量,它的类型为H,继承于Handler类,它定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
private final class H extends Handler {
......
public void handleMessage(Message msg) {
......
switch (msg.what) {
......
}
......
}
......
}
  这个H类就是通过其成员函数handleMessage函数来处理消息的了,后面我们分析消息的处理过程时会看到。
        ActivityThread类的这个mH成员变量是什么时候创建的呢?我们前面在分析应用程序的消息循环时,说到当应用程序进程启动之后,就会加载ActivityThread类的main函数里面,在这个main函数里面,在通过Looper类进入消息循环之前,会在当前进程中创建一个ActivityThread实例:
public final class ActivityThread {
......
public static final void main(String[] args) {
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
}
}
  在创建这个实例的时候,就会同时创建其成员变量mH了:
public final class ActivityThread {
......
final H mH = new H();
......
}
  前面说过,H类继承于Handler类,因此,当创建这个H对象时,会调用Handler类的构造函数,这样就回到2.4 Handler的工作原理

四、总结

4.1 底层流程

至此,我们就从消息循环、消息发送和消息处理三个部分分析完Android应用程序的消息处理机制了,为了更深理解,这里我们对其中的一些要点作一个总结:
         A. Android应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的。
         B. Android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道(Pipe),这个管道的作用是使得Android应用程序主线程在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列有消息需要处理时唤醒应用程序的主线程。
         C. Android应用程序的主线程进入空闲等待状态的方式实际上就是在管道的读端等待管道中有新的内容可读,具体来说就是是通过Linux系统的epoll机制中的epoll_wait函数进行的。
         D. 当往Android应用程序的消息队列中加入新的消息时,会同时往管道中的写端写入内容,通过这种方式就可以唤醒正在等待消息到来的应用程序主线程。
         E. 当应用程序主线程在进入空闲等待前,会认为当前线程处理空闲状态,于是就会调用那些已经注册了的IdleHandler接口,使得应用程序有机会在空闲的时候处理一些事情。
  
Android 基础 十一 Android的消息机制

4.2 上层流程

从我们熟悉的java层面上总结其过程并不陌生,主要结合Handler,Message,Looper,MessageQueue组件阐述如下:
  1.在Looper.loop()方法运行开始后,循环的按照接收顺序取出MessageQueue里面的非NULL的Message。
  2.一开始MessageQueue里面的Message都是NULL的,当Handler.sendMessage(Message)到MessageQueue,该函数里面设置了那个Message对象的target属性是当前Handler对象。随后Looper取出了那个Message,则调用该Message的target指向的Handler的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:见2.4中的图
  1)Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作
  2)Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理。
  3)处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
  4)Handler处理完该Message后,Looper则设置该Message为NULL,以便回收。
 
 
上一篇:多个类定义attr属性重复的问题:Attribute "xxx" has already been defined


下一篇:LeetCode【88. 合并两个有序数组】