Handler

Android消息传递机制,主要用于线程间通信


Looper.java
MessageQueue.java
Message.java

Message

Message类是个final类,不能被继承,同时Message类实现了Parcelable接口,链表结构,内部维护了一个链表缓存池,避免重复创建对象,以sPool作为链表头。

尽管Message的构造器是公开的,但 是获取Message对象的最好方法是调用Message.obtain()或者Handler.obtainMessage(), 这样是从一个 可回收对象池中获取Message对象。

Obtain方法

1
2
3
4
5
6
7
8
9
10
11
12
13
//避免分配新的对象,静态方法
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
} }
return new Message();
}

发送消息

1
2
3
4
//target即handler实例
public void sendToTarget() {
target.sendMessage(this);
}

MessageQueue

MessageQueue依附于Looper,不应该单独创建它,可从Looper类中获取它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public final class MessageQueue {
......
Message mMessages;
......

// native层代码,创建native层中NativeMessageQueue
private native static long nativeInit();
// 构造方法
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed; //android主线程不可退出
mPtr = nativeInit();// 执行native层方法
}

//退出就是当前这个MessageQueue停止服务,将队列中已存在的所有消息全部清空
void quit(boolean safe) {
......

// 两种清除队列中消息的方法
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
}
}
  • removeAllMessagesLocked() ,清除掉队列中所有的消息。
  • removeAllFutureMessagesLocked() ,清除可能还没有被处理的消息。(不会清除当前正在处理的消息,较友好)

消息入队

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
boolean enqueueMessage(Message msg, long when) {
......
// 设置应该被处理的时间
msg.when = when;
// 拿到队列头
Message p = mMessages;

// p等于空说明队列是空的
// when等于0表示强制把此消息插入队列头部,最先处理
// when小于队列头的when说明此消息应该被处理的时间比队列中第一个要处理的时间还早
// 以上情况满足任意一种直接将消息插入队列头部
if (p == null || when == 0 || when < p.when) {
// 将消息插入队列头部
msg.next = p;
mMessages = msg;
}else{
//否则就要按照消息应该被处理的时间插入队列中
//就从头遍历队列根据被处理时间找到它的位置
}

// 判断是否需要唤醒线程
if (needWake) {
nativeWake(mPtr);
}
}

同步屏障

postSyncBarrier(long when) 方法也可以向 队列添加消息,,我们将它添加的特殊Message称为同步屏障。某个Message被调用了 setAsynchronous(true) 后才是异步消息,此方法返回一个token,之后可以根据token找到次消息,然后移除屏障

同步消息受队列限制依次 有序的等待处理,异步消息不受限制。

队列空闲处理器IdleHandler

每次队列中没有消息而进入的阻塞状态,我们叫它为“空闲状态”。

1
2
3
public static interface IdleHandler {
boolean queueIdle();
}

MessageQueue为我们提供了添加和删除IdleHandler的方法:

1
2
3
ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
public void addIdleHandler(@NonNull IdleHandler handler) {}
public void removeIdleHandler(@NonNull IdleHandler handler) {}

消息出队next()方法

// 阻塞线程操作
// 线程将被阻塞的时间
// -1:一直阻塞
// 0:不阻塞
// >0:阻塞nextPollTimeoutMillis 毫秒 int nextPollTimeoutMillis = 0;
nativePollOnce(ptr, nextPollTimeoutMillis);

1.循环开始
2.根据msg的target是不是null判断是否有同步屏障,有的话将指针指向最近的那个异步消息
3.如果msg=null,则nextPollTimeoutMillis=-1,让线程阻塞
4.如果msg!=null,则根据when去处理消息,等待或者直接处理消息
5.如果msg=null或延时消息(now < mMessages.when),会去执行IdleHandler,否则直接return msg

Looper

单纯从MQ中取出消息分发给目标Handler
Looper是线程独立,每个线程只能有一个Looper,会根据自己的存活管理MQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 与当前Looper对应的消息队列 
final MessageQueue mQueue;
// 当前Looper所以的线程
final Thread mThread;

private static void prepare(boolean quitAllowed) {
// 每个线程只能有一个Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created perthread");
}
// 保存实例
sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
// 初始化MessageQueue
mQueue = new MessageQueue(quitAllowed);
// 得到当前线程实例
mThread = Thread.currentThread();
}

MQ和线程都是final修饰的,只能赋值一次
Looper.prepare()初始化之后,可以调用以下方法获得对应实例

1
2
3
4
5
6
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}

是否安全退出Looper在MQ源码中解释过

Looper.loop()比较简单,死循环不断取出消息分发给对应Handler,Looper更想强调的是线程的独立性和唯一性,
用ThreadLocal保证每个线程只有一个Looper实例

Handler

Handler可以在多线程中调用,不管在哪个线程中发消息,都会回到当初初始化的线程中接收消息

Handler有很多初始化方法,最终都是为了给四个变量赋值

1
2
3
4
5
6
7
8
//如果使用其他构造函数,没有传looper实例,则使用Looper.mylopper()获取
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
//此处如果设置了 mAsynchronous 为 true ,那么这个Handler发送的所有消息就都是异步消息。
mAsynchronous = async;
}

2种发送消息的方式(最终都是传msg对象)
postXXX()
sendMessageXXX()
其实这两种方法本质都是发送一个Message对象到原线程,只不过 PostXXX() 方法是发送了一个只有
Runnable callback 属性的Message对象。

1
2
3
4
5
6
7
8
9
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

不管调用何种发送消息的方法,最后真正调用的都是sendMessageAtTime()方法
真正发送的核心方法也就是入队方法是Handler的enqueueMessage()方法

1
2
3
4
5
6
7
8
9
10
11
private boolean enqueueMessage(MessageQueue queue, Message msg, long
uptimeMillis) {
// 将消息的宿主设置为当前Handler自身
msg.target = this;
//如果Handler被设置成了异步就把消息也设置成异步的
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 执行消息队列的入队操作
return queue.enqueueMessage(msg, uptimeMillis);
}

Handler接受消息

Looper.loop()中,执行死循环MQ.next()不断取出msg,然后执行如下:
msg.target.dispatchMessage(msg);

1
2
3
4
5
6
7
8
9
10
11
12
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

可见post比send优先级高

Handler内存泄漏

内部类隐式的持有着外部类的引用,编译器在创建内部类时把外部类的引用传入了其中

当我们在子线程执行一项耗时操作时,用户退出程序,Activity需要被销毁,而Handler还在持有 Activity的引用导致无法回收,就会引发内存泄漏。

  1. 生成内部类时把内部类声明为静态的(静态内部类不会持有外部类的引用)
  2. 使用弱引用来持有外部类引用(弱引用不会阻止JVM回收对象)

Hanlder总结

  1. Message使用缓存池是因为msg使用量巨大,回收可以减少消耗
  2. Message.obtain()可以从缓存池取出msg,在Looper.loop中分发msg,等msg同步执行完,会进行msg的recycle
  3. 真正的阻塞发生在MQ的next()方法中的nativePollOnce方法,采用了linux的epoll机制
    1
    2
    3
    4
    5
    6
    7
    Looper#loop()
    -> MessageQueue#next()
    -> MessageQueue#nativePollOnce()
    -> NativeMessageQueue#pollOnce() //注意,进入 Native 层
    -> Looper#pollOnce()
    -> Looper#pollInner()
    -> epoll_wait()
    epoll全称 eventpoll,是 Linux I/O 多路复用的其中一个实现
    Native层支持监听自定义Fd,比如Input事件就是通过epoll监听socketfd来实现将事件转发到APP进程的
    Java层的nativePollOnce最终调用到JNI层的epoll_wait方法,让线程进入阻塞状态,让出CPU调度