0%

(01)2021.12.9-Handler源码分析---Alvin老师(1)

每个安卓应用都有自己的 vm,所以,每个应用也都有自己的main函数,那么这个 main 函数在哪里呢?在 ActivityThread 里面(省略部分无关代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal {

public static void main(String[] args) {
//省略无关代码
// Install selective syscall interception
AndroidOs.install();

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}

Looper.loop是个死循环,如何才能终止呢?通过MessageQueue.next获取到一个null类型的Message,就退出了​,代码如下:

1
dfsda

messagequeue.quit 也可以达到退出 Looper.loop 死循环的效果。

Message 就是一块内存,内存不分线程(只区分进程吧),因此 Handler 这个过程可以看做是内存共享, 不同线程之间的共享 Msg 这块内存。因此某种程度上,可以

单链表实现的优先级队列

为什么要把 Looper 的初始化只能在 prepare() 方法中进行,而不能让人 new Looper() ?

为什么一个线程只能有一个 Looper ,因为 Looper 存储在 ThreadLocal 中,而我们知道key 是 ThreadLocal 的对象,是唯一的,所以 value 也是唯一的。并且,prepare 的时候,首先通过 threadLocal.get 去获取 Looper ,发现不空就报错,只能 Looper 为空的时候才能 prepare 。

Looper 维持一个 MessageQueue 。一个线程只能创建一个 Looper ,一个 Looper 创建一个 MessageQueue。

Handler 内存泄漏问题的原因?为什么其他内部类没有说有这个问题?比如说 RecyclerView 的 Adapter 里面一般会有一个 ViewHolder 内部类,它为什么不会内存泄漏?

内存泄漏的原因是生命周期不一致。

msg delay 了 20s 才执行,msg 持有的了handler ,handler 是非静态内部类,持有外部对象,所以可能导致内存泄漏。

子线程new handler 要做什么准备?

需要prepare ,有 Looper 才行。

子线程中维护 Looper,消息队列无消息的时候的处理方案是什么?有什么用?

需要调用 MessageQueue.quit, 这时候会将 mQuitting 标志位置为 true 。由于之前已经没有消息了,那么此时应该是处于 nativePollOnce 无限睡眠阶段,quit 方法同样会去调用 nativeWake 方法唤醒,这样就会继续 loope 方法,在里面判断到 mQuitting 为true ,于是return 了一个 null 类型的 Msg ,从而导致 Looper.loop 退出死循环

MessageQueue 为什么不设置容量上限?如果设置了,那么系统的消息都不能进去了,整个系统就死掉了。

两个方面的阻塞:

  • Message 还不到时间,时间到了会自动唤醒

  • MessageQueue 为空,nextPollTimeoutMillis 值为 -1 ,就会进入 nativePollOnce 的无限睡眠了。在enqueueMessage (有新的 Msg 加入的时候)的时候,才会被唤醒(调用nativeWake)

线程阻塞了,所以cpu 就不会来调度它了,节省了cpu 性能。

既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息的时候,各个Handler 可能在不同的线程),那么它的内部是如何保证线程安全的?

  • 首先,1个线程只有一个 MessageQueue

  • 其次,MessageQueue 操作加锁了。在 MessageQueue 中,不论是 enqueue 方法,还是 next 方法,都会在里面锁代码块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 boolean enqueueMessage(Message msg, long when) {
//省略无关代码

synchronized (this) {
//省略无关代码
}
}


Message next() {
//省略无关代码
for (;;) {
synchronized (this) {
//省略无关代码
}
}
}

从代码可以看出,锁的是整个 MessageQueue 对象,所以,对于同一个 MessageQueue 来说,每次只能一个线程存/取。

为什么取的时候也要加锁?明明都是从头取?

这是因为你取的时候,我这边有线程正在加入新的 Msg ,此时就存在同步问题。

谢谢你的鼓励