Message
Message中一些常见的参数与API
public final class Message implements Parcelable{
public int what; // 消息的id
public int arg1; // 整数参数1
public int arg2; // 整数参数2
public Object obj; // 任意类型参数
int flags; // 消息标记:正在使用异步还是同步
Bundle data; //消息附带的额外数据
long when; // 消息被处理的时间
Handler target; // 消息的接收与处理者
Messge next; // 下一个消息的指针
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
public static Messgae obtain(){} // 复用消息池中的消息
void recycleUnchecked(){} // 回收并清理消息(msg.target = null;sPool = this)
public void sendToTarget(){target.sendMessage(this);}
}
- 由于Message定义了下一个消息的指针,因此它组成了一个消息单链表;
- 在Looper.loop()从消息队列中读取一个消息后,首先调用msg.target.dispatchMessage(msg) 将消息回传给 handleMessage(),最后调用msg.recycleUncheckd() 对消息进行清理回收。
- 使用handler.obtainMessage()复用消息时,其实是从消息池中获取一个消息再利用。
MessageQueue
Message中一些常见的参数与API
public final class MessageQueue{
public boolean mQuitAllowed;
Message mMessages; // 当前要处理的消息
private boolean mBlocked; // 表明线程是否阻塞在pollOnce上
private boolean mQuitting; // 是否主动退出循环
private void native nativePollOnce() // 阻塞线程
private void native static nativeWake; // 唤醒被阻塞的线程
public void enqueueMessage(msg){} // 添加消息至消息队列
public int postSyncBarrier(){} // 发送一个同步屏障,阻塞同步消息
Message next(){} // 读取消息
}
- MessageQueue采用链表来管理消息,其mMessages是当前要处理的消息;
- 调用next()函数从消息链表中读取一个消息时,若队列中没有消息,则会阻塞在nativePollOnce()上;若队列中有消息,则会检测当前消息是不是同步屏障 ,若是则过滤所有的同步消息,只迭代异步消息,然后从链表中删除并得到该消息。
- 调用enqueueMessage()向消息队列中添加消息,若队列中还没有消息,则直接作为消息链表的head,并调用nativeWake()唤醒阻塞在nativePollOnce()上的线程;若消息队列中有消息,比较 msg.when ,按照时间顺序添加进队列,检测是否需要调用nativeWake()。
Handler
Handler中的重要参数和API
public class Handler{
final Looper mLooper; // 循环读取消息队列中的消息
final MessageQueue mQueue; // Looper内置的消息队列
final Callback mCallback; // 处理消息的回调
// 指定Looper的构造函数
public Handler(Looper looper){
this.mLooper = looper;
this.mQueue = looper.mQueue;
***
}
// 不指定Looper的构造函数
public Handler(){
this.mLooper = Looper.myLooper(); // 通过ThreadLocal.get()获取本线程的Looper
if(mLooper == null){
throw RuntimeExcption("");
}
this.mQueue = mLooper.mQueue;
***
}
// 向消息队列添加消息
// post系列
pulbic final boolean postDelayed(Runnable runnable, long delayMillis){
return sendMessageDelayed(getPostMessage(runnable), delayMillis);
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r; // 指定了回调处理者
return m;
}
// send系列
public final boolean sendMessage(Message msg){
return sendMessageDelayed(getPostMessage(runnable), 0);
}
// 移除指定消息
public final void removeMessage(int mhat){
mQueue.removeMessages(this, what, null);
}
// 移除所有的消息
public final void removeCallbacksAndMessages(Handler handler, Object obj){
mQueue.removeCallbacksAndMessage(this,obj);
}
// 复用Message
public final Message obtainMessage(){
return Message.obtain();
}
// 分发消息
public void dispatchMessage(Message msg){
if(msg.callback != null){ // 优先处理指定了消息处理者的消息
handleCallback(msg);
} else {
if(mCallback != null){
mCallback.handleMessage(msg);
}else{
handleMessage(msg);
}
}
}
// 处理消息的回调,子类必须实现
public void handleMessage(Message msg){}
}
- Handler必须在有Looper的线程中创建,可以在任意线程发送消息,处理消息的线程都是其对应的Looper所在的线程;由对应线程的Looper.loop()循环从消息队列中读取消息,接着调用 dispatchMessage 分发并处理消息;
- post系列的函数为待发送的消息指定了回调处理者Runnable,因此该消息交由handleCallback(msg)处理;send系列函数则全部交由handleMessage(msg)处理。
- 若在Activity或者Fragment中使用Handler向消息队列添加了消息,则在onDestrory时,主动调用removeMessage()对消息进行清理回收,防止内存泄露;
Looper
一个线程只能有一个Looper用来作为线程循环,新创建的Looper实例将保存到该线程的线程本地存储(ThreadLocal)中。
Looper拥有一个消息队列MessageQueue,该消息队列在Looper的构造方法中创建,属于该Looper对象所有。一个Looper只能有一个MessageQueue.
线程通过Looper.loop()循环处理Looper维护的MessageQueue中的Message,消息发送到那个Looper的消息队列中和如何处理该消息,则有消息发送者指定。指定了Looper就意味着指定了处理消息的线程;为消息指定的Handler则决定了用户如何处理该消息。
源码分析:
public final class Looper{
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); // 线程本地变量
private static Looper sMainLooper; // 主线程的Looper实例
final MessageQueue mQueue; // Looper维护的消息队列
final Thread mThread; // Looper所处的线程
private Looper(boolean quitAllowed){
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static void prepare(){
// 确保一个线程只能拥有一个Looper实例
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 通过ThreadLocal维护一个Looper实例
sThreadLocal.set(new Looper(true));
}
public static void prepareMainLooper(){
prepare();
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 将ThreadLocal维护的所属主线程的Looper实例改为类变量。
sMainLooper = myLooper();
// 由此可见,在Activity中即便不使用静态+弱引用的方式创建Handler也不一定引起内存泄露。
}
}
public static Looper myLooper(){
return sThreadLocal.get();
}
// 循环读取维护的消息队列中的消息
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(); // 可能会阻塞
if(msg == null){ // 表明线程退出
return;
}
***
try{
// 分发并处理消息msg
msg.target.dispatchMessage(msg);
}finally{
}
// 清理并回收该消息,将该消息添加进消息池
msg.recyleUncheckd();
}
}
}
问题:
- 如果msg.next会阻塞主线程,那为什么程序运行时没感到卡顿那?
Handler的阻塞是通过底层的Looper.pollOnce(long delayMillis)实现的。pollOnce采用 epoll 机制(NIO机制的一种),在读取端阻塞时不会消耗CPU资源,所以不存在竞争资源的问题,因此不会感到卡顿。 - Handler必须在一个含有Looper的线程中创建,为什么这么设计?
首先,我们知道,Handler是消息的发送者也是消息的处理者,而消息是消息队列维护的,消息队列又是在Looper的构造方法中创建的,也就是说Looper自己维护了消息队列。因此,Handler必须在一个初始化并执行了Looper的线程中创建。
总结
Handler通信机制采用循环等待/唤醒机制,在一个线程中通过handler的post/send系列函数向Looper维护的消息队列中添加消息时,会唤醒阻塞在nativePollOnce处的线程,在Looper.loop循环中读取消息,然后使用handler分发处理该消息.