在Android中,主线程UI线程不是线程安全的,对UI的更新只能在UI线程中执行,在工作线程中执行UI更新操作,会抛出android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 错误。
为了实现工作线程对UI线程的操作,以及线程间通信,Android中设计了一套消息机制。
Android消息机制主要有Looper、Handler、Message和MessageQueue几个类组成。
借用下别人的图片,各组件的关系如下:
Looper
每个线程只能有一个Looper(其实Looper是一个ThreadLocal对象),Looper主要实现对MessageQueue中的Message信息的消费。
UI线程启动的时候,默认绑定了一个Looper。
对于工作线程可通过Looper.prepare()和Looper.loop()简单的两步,把线程变为LooperThread,实现线程对消息的消费。并为工作线程实例化Handler对象,用于消息的发送和处理。
Looper.prepare()静态方法:
private static final ThreadLocal sThreadLocal = new ThreadLocal();
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
final MessageQueue mQueue;
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
Looper初始化的时候,会初始化本线程对应的MessageQueue。
public static final void loop() {
Looper me = myLooper();
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();
while (true) {
Message msg = queue.next(); // might block
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.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("Looper", "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可以通过静态方法getMainLooper()获取UI线程的Looper对象,通过静态方法myLooper(),获取本线程的Looper对象。
Handler
Handler负责对信息的发送和处理,工作线程可以引用UI线程的Handler实例对象,通过post、postDelayed和sendMessage方法,把消息发送到MessageQueue中。
其中post*方法,传递的为Runnable对象,Runnable对象被封装为Message的callback属性,所以最终发送到MessageQueue中的还是Message对象。
Handler通过覆复写handleMessage方法,作为处理Message的回调方法,实现对Message的处理。
消息处理步骤:
- Looper在消费Message的时候,先判断Message中是否有callback方法,如果有,调用callback,处理消息。
- 如果Handler中有mCallback方法,则调用该方法处理消息。
- 否则,调用handleMessage方法处理消息。
一个线程可以有多个Handler对象,同一个Handler对象发送的Message,由对象自身的handleMessage方法进行处理。
handler.despatchMessage实例方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在工作线程中,需要向UI线程发送消息是,不一定要在UI线程中建立Handler对象,并传递到工作现在。
在工作线程中,可以通过Looper.getMainLooper()获取到UI线程的Looper对象,并通过此Looper对象,实例化Handler对象,绑定到UI线程的Looper和MessageQueue。
class MyThread extends Thread{
public void run(){
Looper looper = Looper.getMainLooper(); //主线程的Looper对象
//这里以主线程的Looper对象创建了handler,
//所以,这个handler发送的Message会被传递给主线程的MessageQueue。
handler = new MyHandler(looper);
//构建Message对象
//第一个参数:是自己指定的message代号,方便在handler选择性地接收
//第二三个参数没有什么意义
//第四个参数需要封装的对象
Message msg = handler.obtainMessage(1,1,1,"其他线程发消息了");
handler.sendMessage(msg); //发送消息
}
}
Message
Message是消息的载体,Message中有String arg1、String arg2、Object obj、int what等几个属性,用户传递简单参数。复杂参数则使用Bundle传递。
Message可以通过静态方法Message.obtain()和实例方法handler.obtainMessage()等方法获取,可以实现对Message对象的复用。通过实例方法message.cycle(),实现Message的回收复用。
实例方法message.sendToTarget()方法等同于实例方法handler.sendMessage()
MessageQueue
MessageQueue是和Looper一一关联的对象,通过消息队列机制对消息进行存储。