Android系统不能在子线程中更新UI界面,因为Android的UI控件不是线程安全的。这种情况下我们可以在UI线程中创建一个Handler,子线程通过这个Handler发送Message到UI线程,通知UI线程更新UI界面。这是Android的消息机制一个非常典型的应用场景。在Android中,AsyncTask、IntentService等也都用到了Android的消息机制。通过源码分析Android的消息机制,我们可以深入了解一条Message从发送到最终被处理的完整过程。
Android的消息机制是由Handler、MessageQueue和Looper组成的一整套消息发送、消息处理的消息系统。其中,MessageQueue用来存储Message,Looper用来为线程提供消息循环,Handler用来发送和处理Message。
在Android中,可以为每个线程创建一个独立的消息循环。这个功能可以通过一个ThreadLocal变量来实现。ThreadLocal变量可以为每个线程提供一个独立的对象副本,它们之间的操作是互不干扰的。通常,一个类中的ThreadLocal变量是private static类型的。我们可以通过ThreadLocal变量的get()方法来获取当前线程的对象副本,通过set()方法来设置当前线程的对象副本。
MessageQueue的作用是存储Message。我们可以通过Looper.myQueue()静态方法来获取当前线程的MessageQueue。Message并不是直接被添加到MessageQueue中的,而是通过与线程相关联的Handler对象来添加的。比如:Handler的sendMessage()、sendMessageDelayed()等方法。
对于MessageQueue,我们主要关注两个方法:添加一条Message的enqueueMessage()方法和获取下一条Message的next()方法。我们先来看添加一条Message的enqueueMessage()方法。
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.");
}
...
}
可以看到,添加到MessageQueue的Message的target不能为null,否则将抛出异常。Message的target其实是一个Handler对象,这条Message最终就是由这个Handler对象来处理的。我们通常是通过Handler对象的一系列obtainMessage()方法来获取一条Message。这种方式不仅高效地从全局的Message池中获取一条Message,而且它设置了这条Message的target为这个Handler对象。
同时,添加到MessageQueue的Message不能处于使用中的状态,否则也将抛出异常。一条新的Message当它被添加到MessageQueue中时,它就一直处于使用中的状态。接着往下看enqueueMessage()方法。
boolean enqueueMessage(Message msg, long when) {
...
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;
}
...
}
return true;
}
如果mQuitting变量为true,即MessageQueue退出了,那么添加Message将失败。反之,Message将被添加到MessageQueue中。
首先,调用Message的markInUse()方法标记Message处于使用中的状态。然后,将Message添加到MessageQueue维护的mMessages链表中。这个链表按照Message添加进来时的when时间变量从小到大的顺序来存储Message。
接着,我们来看获取下一条Message的next()方法。
Message next() {
...
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
}
...
}
}
可以看到,next()方法是一个死循环,即默认情况下程序会阻塞在这里。
首先,从mMessages链表中取出第一条Message。如果Message不为null,并且达到了Message的when时间,那么就返回这条Message。当mQuitting变量为true,即MessageQueue退出时,next()方法返回null。
Looper的作用是为一个线程提供一个消息循环。默认情况下,线程没有与它相关联的Looper。在线程中调用Looper.prepare()静态方法可以为该线程创建一个Looper。调用Looper.loop()静态方法可以启动该线程的消息循环。调用Looper.myLooper()静态方法可以获取该线程的Looper。
在Android中,是通过Handler对象来与Looper进行通信的。下面是一个实现Looper线程的典型例子:
class LooperThread extends Thread {
public Handler mHandler;
@Override
public void run() {
Looper.prepare();
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
我们先来看创建一个Looper的Looper.prepare()静态方法。
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal sThreadLocal = new ThreadLocal();
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));
}
可以看到,Looper类是通过一个静态的ThreadLocal变量来为每个线程存储一个独立的Looper对象。每个线程只能创建一个Looper对象,否则将抛出异常。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper的构造方法中,创建了与线程相关联的MessageQueue。
接着我们来看启动消息循环的Looper.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;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
可以看到,loop()方法也是一个死循环,它不停地从MessageQueue中取出Message进行处理。
首先,通过myLooper()静态方法获取当前线程的Looper。然后,从这个Looper中取出当前线程的MessageQueue。最后,在死循环中通过MessageQueue的next()方法不停地获取下一条Message进行处理。当msg变量为null,即MessageQueue退出时,Looper退出循环。
根据前面的分析我们知道,Message的target就是一个Handler对象,所以最终Message是在Handler对象的dispatchMessage()方法中被处理的。由于Looper.loop()静态方法是在线程之中执行的,所以Message最终是在与Handler相关联的线程之中被处理的。如果Handler发送Message是在另外一个线程中,那么通过Handler就可以做到线程间通信。
注意:当不再使用Looper时,记得要调用quit()方法来结束消息循环。
public void quit() {
mQueue.quit(false);
}
可以看到,quit()方法其实是调用了MessageQueue的quit()方法。MessageQueue的quit()方法退出了MessageQueue,使得MessageQueue的next()方法返回了null,最终结束了消息循环。
Handler可以用来发送和处理Message和Runnable。每个Handler都跟一个线程和线程的MessageQueue相关联。当你创建一个新的Handler时,它就自动与一个线程和线程的MessageQueue绑定在一起。然后,我们就可以通过该Handler发送Message和投递Runnable到这个MessageQueue中。当它们从MessageQueue中被取出来时这个Handler就可以处理或者执行它们。
通常,Handler有两个作用:
我们先来看Handler的两个构造方法:默认构造方法和带Callback的构造方法。这两个构造方法会将Handler对象与当前线程的Looper相关联,也即与当前线程相关联。
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
...
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先通过Looper.myLooper()静态方法获取当前线程的Looper。如果当前线程没有创建Looper,即没有调用过Looper.prepare()静态方法,那么将抛出异常。然后,存储了当前线程的MessageQueue。如果设置了Callback参数,那么将其设置给mCallback成员变量。
接着我们来看Handler带Looper参数的构造方法。
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
带Looper参数的构造方法其实就是创建了一个与指定线程相关联的Handler。
Handler发送Message可以通过sendMessage()、sendMessageDelayed()、sendMessageAtTime()和sendEmptyMessage()等方法。投递Runnable可以通过post()、postDelayed()和postAtTime()等方法。
接着我们来看发送一条Message的sendMessage()方法。
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);
}
可以看到,sendMessage()方法调用了sendMessageDelayed()方法。sendMessageDelayed()方法又调用了sendMessageAtTime()方法。sendMessageAtTime()方法最终调用了enqueueMessage()方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在enqueueMessage()方法中,先是指定了这条Message的target为此Handler对象。最终调用了MessageQueue的enqueueMessage()方法将Message添加到了MessageQueue之中。
接着我们来看投递一个Runnable的post()方法。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到,post()方法与sendMessage()方法一样,也是调用了sendMessageDelayed()方法。Runnable对象被包装成一条Message,它被设置给了Message的callback成员变量。注意Message的callback成员变量与mCallback成员变量的不同。
最后我们来看Handler处理Message的过程。根据前面的分析我们知道,最终Handler是在dispatchMessage()方法中处理Message的。
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();
}
可以看到,如果Message的callback成员变量不为null,即通过Handler投递了Runnable,那么执行此Runnable。
反之,就是通过Handler发送了Message。如果Message的mCallback成员变量不为null,即在创建Handler的时候设置了Callback参数,那么先调用此Callback对象的handleMessage()方法来处理Message。如果在创建Handler的时候没有设置Callback参数,或者Callback对象的handleMessage()方法返回了false,那么将调用Handler对象子类重写的handleMessage()方法来处理Message。
Android的消息机制由Handler、MessageQueue和Looper三个部分组成。三个部分各司其职,共同完成了Android系统的线程的消息循环。我们可以使用Android的消息机制来进行线程间通信。
这里举了一个简单的例子来实践Android的消息机制。例子代码地址:https://github.com/chongyucaiyan/HandlerDemo
例子的主要功能是:在子线程中更新计数(模拟耗时任务),然后通知UI线程更新界面。我们先来看一下不通过Handler直接在子线程中更新UI界面会产生什么异常。代码如下:
public class MainActivity extends AppCompatActivity {
private TextView mTvCount;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initContentView();
// 在子线程中更新计数
updateCount();
}
private void initContentView() {
mTvCount = (TextView) findViewById(R.id.tv_main_count);
setCount(0);
}
private void setCount(int count) {
mTvCount.setText(String.format(getString(R.string.main_count_format), count));
}
private void updateCount() {
new Thread() {
@Override
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(1000);
count++;
setCount(count);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
}.start();
}
}
运行例子,应用崩溃。打印的崩溃信息如下图所示:
完整的异常信息是:android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 大概意思就是只有创建UI界面的那个线程才能更新UI界面。Android系统是在主线程(UI线程)中创建UI界面的,所以不能在子线程中直接更新UI界面。
修改代码,在UI线程中创建一个Handler,子线程通过这个Handler发送Message来通知UI线程更新界面。为了防止内存泄漏,MainHandler类声明为static内部类并且弱引用外部类。代码如下:
public class MainActivity extends AppCompatActivity {
private static final int SET_COUNT = 1;
private TextView mTvCount;
private MainHandler mMainHandler = new MainHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initContentView();
// 在子线程中更新计数
updateCount();
}
private void initContentView() {
mTvCount = (TextView) findViewById(R.id.tv_main_count);
setCount(0);
}
private void setCount(int count) {
mTvCount.setText(String.format(getString(R.string.main_count_format), count));
}
private void updateCount() {
new Thread() {
@Override
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(1000);
count++;
Message message = mMainHandler.obtainMessage(SET_COUNT, count, -1);
mMainHandler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
}.start();
}
private static class MainHandler extends Handler {
private WeakReference mMainActivityRef;
MainHandler(MainActivity mainActivity) {
mMainActivityRef = new WeakReference<>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SET_COUNT:
MainActivity mainActivity = mMainActivityRef.get();
if (mainActivity != null) {
mainActivity.setCount(msg.arg1);
}
break;
default:
break;
}
}
}
}
运行例子,可以看到应用正常更新UI界面。如下图所示: