1、消息是如何添加到队列的
handler 发送一个message 时,会调用sendMessage等方法,这些方法中最终都会调用 Handler 中的 enqueueMessage 方法,我们看一下 enqueueMessage 方法做了什么:
//Handler
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//...
//这里执行MessageQueue的 enqueueMessage
return queue.enqueueMessage(msg, uptimeMillis);
}
实际上就是调用了MessageQueue的enqueueMessage:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
从代码中我们也可以看出MessageQueue 收到消息以后,会根据时间进行排列。
2、Handler的消息队列在哪创建的
回到创建Handler的地方,它的构造方法:
//Handler
public Handler() {
this(null, false);
}
//Handler
public Handler(Callback callback, boolean async) {
//...
//获取当前的looper
mLooper = Looper.myLooper();
//...
//获取looper 的 MessageQueue
mQueue = mLooper.mQueue;
//...
}
//Looper
final MessageQueue mQueue;
private Looper(boolean quitAllowed) {
//在这里创建了一个 MessageQueue
mQueue = new MessageQueue(quitAllowed);
//...
}
可以看到 Handler其实是拿着Looper 的MessageQueue当做自己的MessageQueue。
3、Loope有什么作用
消息被有序的添加到了消息队列中,而Looper就是负责将消息从消息队列中取出。当执行Looper的loop()方法,Looper会从消息队列中取出消息,然后交给handler的dispatchMessage去处理消息。
//Looper
public static void loop() {
//...
for (;;) {
//从消息队列中获取消息
Message msg = queue.next(); // might block
//...
try {
//msg.traget 就是Handler
//使用 Handler 的 dispatchMessage() 处理消息
msg.target.dispatchMessage(msg);
//...
} catch (Exception exception) {
//...
}
//...
}
}
4、一个线程有几个Looper
要想知道有几个Lopper,肯定要先知道Looper在哪里创建。Looper有一个prepare方法:
//Looper
public static void prepare() {
prepare(true);
}
在这里会创建一个新的Looper 并且设置到了ThreadLocal:
//Looper
private static void prepare(boolean quitAllowed) {
//通过 sThreadLocal get 检查是否已经有 looper
if (sThreadLocal.get() != null) {
//如果已经有了 就抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
//没有的话 就设置一个新的Looper
sThreadLocal.set(new Looper(quitAllowed));
}
在ThreadLocal可以看到是以map的形式去保存,保证了一个线程只有一个map,又将looper和ThreadLocal进行绑定
//ThreadLocal
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取 ThreadLocalMap
ThreadLocalMap map = getMap(t);
//有的话 就将当前的 ThreadLocal 和 Looper 绑定在一起,
if (map != null)
//set 以后 在上面 sThreadLocal.get() 就不会在为null了
map.set(this, value);
else
//没有的话 创建一个 ThreadLocalMap 在绑定在一起
createMap(t, value);
}
看到Looper中的 sThreadLocal:
//Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
他是一个静态的 final 保证了 一个Looper只有一个 sThreadLocal,最终保证了一个线程只有一个Looper。
5、主线程什么时候执行preapre
想要使用Looper,肯定需要先prepare 去创建一个Looper,那么主线程如何创建Looper的呢?我们知道 java 程序的入口是 main 方法, 对于Android来说,其实也有一个main 方法,他的位置在 ActivityThread:
//ActivityThread
public static void main(String[] args) {
//...
//可以看到在这里 程序启动以后,Android 系统帮我们将主线程的Looper prepare
Looper.prepareMainLooper();
//...
//然后帮助我们启动了 loop
Looper.loop();
//...
}
6、Handler内存泄露
Handler为什么会有可能导致内存泄露? 我们知道 内部类会持有外部类的引用,当我们做一个延时任务,延时10S,然后在10S内退出Activity,在我们sendMessage的时候,handler对象被传递给msg 如👇所示,然后被存放在MessageQueue中。在这10S内,即使Activity销毁了,但是引用关系依然被保存在MessageQueue中,那么即使Activity销毁了,他的对象依然不会被GC销毁,因为他依然被引用。就导致内存未被回收。
/Handler
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//这里 将 handler 本身的对象 传给 msg 的target
msg.target = this;
//...
}
复制代码那么如何处理Handler内存泄露呢
- 将Handler改成静态类。原因是因为静态类不会持有外部类的引用
- 继承Handler,将Activity作为弱引用使用
private static class AppHandler extends Handler { //弱引用,在垃圾回收时,被回收 WeakReference<Activity> activity; AppHandler(Activity activity){ this.activity=new WeakReference<Activity>(activity); } public void handleMessage(Message message){ switch (message.what){ //todo } } }
- 在界面退出的时候,调用Handler的removeMessages方法
// 清空消息队列,移除对外部类的引用 @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); } //Handler源码中removeCallbacksAndMessages()注释含义 /** * Remove any pending posts of callbacks and sent messages whose * <var>obj</var> is <var>token</var>. If <var>token</var> is null, * all callbacks and messages will be removed. */ public final void removeCallbacksAndMessages(Object token) { mQueue.removeCallbacksAndMessages(this, token); }
7、消息队列没有消息时Handler如何挂起
Looper从MessageQueue中获取message,当获取不到message的时候,会将 nextPollTimeoutMillis置成-1,然后进入下次循环,当执行nativePollOnce方法时候,如果nextPollTimeoutMillis==-1那么就会执行Linux的epoll机制,让线程处于挂起状态,阻塞线程。
//MessageQueue
Message next() {
for (;;) {
//step3: nextPollTimeoutMillis == -1 执行native 函数,
//执行 linux epoll 机制,线程处于等待状态,线程挂起
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//...
if (msg != null) {
} else {
// step1:如果没有消息 nextPollTimeoutMillis 变成-1
nextPollTimeoutMillis = -1;
}
}
}
}
//Looper
public static void loop() {
for (;;) {
//step4:这里也就挂起了
Message msg = queue.next(); // might block
}
}
8、Handler如何退出
使用looper去执行quit方法退出
handler.looper.quit()
//Looper
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
//MessageQueue
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
//step1:将mQuitting 变量变成true
mQuitting = true;
//step2:删除所有的消息
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
//step3:唤醒线程
nativeWake(mPtr);
}
}
//MessageQueue
Message next() {
for (;;) {
//step4:线程被唤醒。继续执行
nativePollOnce(ptr, nextPollTimeoutMillis);
//step5:检查到状态是 true 返回null 出去
if (mQuitting) {
dispose();
return null;
}
}
}
//Looper
public static void loop() {
for (;;) {
//step6:这里也被唤醒获取到message == null
Message msg = queue.next(); // might block
//step7:最终在这里🔚循环
if (msg == null) {
return;
}
}
}
Looper会先将消息队列中的消息全部清空,然后使用nativeWake的native方法唤醒线程,在上面我们介绍了,当消息队列中没有消息的时候,线程会挂起,处于等待状态,当我们唤醒以后,Looper的loop方法会继续执行下去,然后从MessageQueue中获取到一个null的Message,最终将Looper的loop()方法退出。
9、主线程能够Quit么?
我们知道了 主线程是在ActivityThread的main方法中执行了Looper.prepareMainLooper()创建的Looper:
//Looper
@Deprecated
public static void prepareMainLooper() {
//step1: 注意看这里是一个false
prepare(false);
}
//Looper
private static void prepare(boolean quitAllowed) {
//step2:new的Looper传入的是false
sThreadLocal.set(new Looper(quitAllowed));
}
//Looper
private Looper(boolean quitAllowed) {
//step3:创建的MessageQueue 传入的也是false
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//MessageQueue
MessageQueue(boolean quitAllowed) {
//step4:将mQuitAllowed 变量变成了false
mQuitAllowed = quitAllowed;
}
//MessageQueue
void quit(boolean safe) {
//step5:如果是false 就是主线程 会直接抛出错误
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
}
回头在看一下 Looper的prepare方法,只有主线程可以创建一个不可以quit的MessageQueue,其他线程创建的都是可以quit的
//Looper
//公开方法 prepare 传入的是true
public static void prepare() {
prepare(true);
}
//私有方法
private static void prepare(boolean quitAllowed)
//主线程 传入的是false
public static void prepareMainLooper() {
prepare(false);
}
10、为什么设计主线程不能被quit
在ActivityThread中,定义了一个H的类,继承了Handler,这个H的handler执行了Android所有的主要事件,比如广播,service,Activity生命周期等都是在这里进行处理,所以不能把主线程quit:
//ActivityThread
class H extends Handler {
}
11、消息如何知道是由哪个Handler发送的?
一个线程可以有多个Handler,想new几个都可以,在我们往MessageQueue中添加消息的时候,会加入一个target标记是哪个handler发送的:
//Handler
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//step1:在这里 就标记了是哪一个handler 发送的
msg.target = this;
//...
}
//Looper
public static void loop() {
//...
for (;;) {
//...
try {
//step2:这里就对应起来是哪一个handler 发送的message
msg.target.dispatchMessage(msg);
//...
} catch (Exception exception) {
//...
}
//...
}
12、Handler如何确保线程安全的
//MessageQueue
boolean enqueueMessage(Message msg, long when) {
//step1:通过加锁的方式,保证了添加消息到消息队列的安全
synchronized (this) {
}
}
//MessageQueue
Message next() {
for (;;) {
//step2:通过锁的方式保证了读取消息的安全
synchronized (this) {
}
}
}
13、Message如何复用的
看一下我们quit的时候,是怎么从消息队列中清空消息的
//MessageQueue
void quit(boolean safe) {
synchronized (this) {
//step1: 清除所有的消息
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
}
}
//MessageQueue
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
//step2:执行message的方法
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
//Message
void recycleUnchecked() {
//step3:将所有的变量全部清空
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//默认50个Message
if (sPoolSize < MAX_POOL_SIZE) {
//step4:将已经清空状态的Message 放到一个新的链表中
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
使用obtain方法会从之前清空状态的链表中取出一个Message去使用,减少创建Message带来的内存消耗。
//Message
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
//step5:从已经清空状态的链表中取出一个Message使用
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
这种设计模式称为享元设计模式
14、为什么主线程loop不会导致ANR
首先要知道ANR是怎么出现的,ANR出现的条件有两个
- 5秒内没有响应输入的事件,触摸反馈等
- 广播10秒内没有执行完毕
在上面我们分析知道,所有的事件都是由Handler进行分发,在主线程上,发送一个事件,这个事件耗时,将主线程的loop()给卡主,让他只能执行当前任务,不能去处理其他事件就出现了ANR。
ANR的本质是由于不能及时处理消息导致的,和他的loop是没有任何关系的。