概述
Handler在开发中最常用的就是拿来更新UI了。Handler的运行需要MessageQueue和Looper的支撑。Handler创建的时候会采用当前线程的Looper构造消息循环系统。但线程是默认没有Looper的,需要我们手动创建。除非是主线程,也就是UI线程,它就是ActivityThread。ActivityThread被建立的时候系统就会初始化Looper,所以主线程可以默认使用Handler。Handler创建完毕后,通过Handler的post方法将一个Runnable对象投递到Looper中处理,或者用handler的send方法发送消息,其实post方法最终也是通过send方法调用。
ThreadLocal
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程存储数据,所以也只能在指定线程中获取相、应的数据,其他线程则没有办法。平常用到ThreadLocal的地方不多,但有时也挺好用。如果某些数据是以线程为作用域并且不同线程有不同数据副本时,就可以使用ThreaLocal。比如Looper。不同线程有不同的Looper,通过ThreaLocal就可以轻松实现Looper在不同线程中的存取。
我们演示一下ThreadLocal的用法。
private ThreadLocal<boolean> mBooleanThreadLocal = new ThreadLocal<boolean>();
接着在主线程,子线程1,子线程2中分别设置和获取它的值。
mBooleanThreadLocal.set(true);
Log.d("TAG","(Thread main)mBooleanThreadLocal ="+mBooleanThreadLocal.get());
new Thread(){
@override
public void run(){
mBooleanThreadLocal.set(false);
Log.d("TAG","(Thread1)mBooleanThreadLocal ="+mBooleanThreadLocal.get());
}
}.start();
new Thread(){
@override
public void run(){
mBooleanThreadLocal.set(false);
Log.d("TAG","(Thread2)mBooleanThreadLocal ="+mBooleanThreadLocal.get());
}
}.start();
上面代码输出为
(Thread main)mBooleanThreadLocal =true;
(Thread1)mBooleanThreadLocal =false;
(Thread2)mBooleanThreadLocal =null;
看完你就知道ThreadLocal的神奇之处。同一个ThreadLocal对象得到的值却不同。其实是因为不同线程调用ThreadLocal的get方法,ThreadLocal内部会从各自线程取出一个数组,再从数组中根据当前ThreadLocal的索引查找对应的value值。显然不同线程的数组是不同的。所以ThreadLocal在不同线程中维护一套数据的副本是互不干扰的。
MessageQueue
MessageQueue主要包括两个操作,插入和读取,对应的方法为enqueueMessage和next。enqueueMessage的作用就是往消息队列中插入一条消息。next的作用是取出一则消息读取,并从消息队列中移除。MessageQueue虽然叫做消息队列,实际上却是用单链表的数据结构,因为执行插入和删除效率比较高。
Looper
Looper创建时MessageQueue会跟着创建。并把当前线程的对象保存起来。
在线程中使用Looper的方式如下,注意这里的Handler并不能更新UI。
new Thread(){
@override
public void run(){
Looper.prepare();//创建Looper对象
Handler handler = new Handler();
Looper.loop();//Looper开始工作
}
}.start();
Looper还提供了退出的方法,quit和quitSafely。调用quit方法会直接退出Looper,而quitSafely只是设定一个退出标记,然后等消息队列中的已有消息全部处理完毕后才退出。Looper退出后,Handler发送的消息会失败。如果在子线程中手动创建Looper,那么所有事情完毕后应调用quit方法终止消息循环。而如果退出Looper后,线程也就被终止了。
Looper.loop()是一个死循环,只有MessageQueue的next方法返回了null才能跳出循环。当Looper的quit方法调用时Looper会调用MeassageQueue的quit方法来通知消息队列退出,此时next方法就会返回null。
Handler的工作原理
先看一下发送消息过程的源码
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方法被调用。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//处理sendMessage();方法发送的消息
handleMessage(msg);
}
}
看上面的代码,Message的callback是一个Runnable对象,就是post方法传递过来的Runnable对象。handleCallback的方法很简单,就是执行run方法。
private static void handleCallback(Message message) {
message.callback.run();
}
Callback是一个接口。定义为
public interface Callback {
public boolean handleMessage(Message msg);
}
Callback的意义在于可以创建Handler的实例而不用派生Handler的子类。使用方式如下
Handler handler= new Hanlder(Callback);
平常我们一般都是创建Handler的子类并重写handleMessage方法。Callback为我们提供了另一种方式。
我们借助一张图片来理解一下Hanlder消息处理的流程,相信不难理解。
Handler消息处理流程以上总结大部分来自《Android开发艺术探索》,想要理解更仔细请翻阅原书。
网友评论