优质文章
Android异步通信:图文详解Handler工作原理_Carson带你学Android-CSDN博客 👍
Android Handler 机制(一):Handler 运行机制完整梳理 - 灰色飘零 - 博客园
Android Handler机制 - MessageQueue如何处理消息_未子涵的博客-CSDN博客
Android应用程序消息处理机制 - SegmentFault 思否
源码解读epoll内核机制 - Gityuan博客 | 袁辉辉的技术博客
常见问题
提示
下面是一些常见的问题,可以先尝试的先自我回答一下,点击题目即可查看答案。
主线程为什么不用初始化Looper?
因为应用在启动的过程中就已经初始化主线程Looper了。
每个java应用程序都是有一个main方法入口,Android是基于Java的程序也不例外。Android程序的入口在ActivityThread的main方法中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/**
* main方法中先初始化主线程Looper,
* 新建ActivityThread对象,然后再启动Looper,这样主线程的Looper在程序启动的时候就跑起来了。
* 我们不需要再去初始化主线程Looper。
*/
public static void main(String[] args) {
...
// 初始化主线程Looper
Looper.prepareMainLooper();
...
// 新建一个ActivityThread对象
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
// 获取ActivityThread的Handler,也是他的内部类H
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
// 如果loop方法结束则抛出异常,程序结束
throw new RuntimeException("Main thread loop unexpectedly exited");
}
|
为什么主线程的Looper是一个死循环,但是却不会ANR?
因为Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。
主线程中的Looper.loop()死循环为什么不会导致ANR?_威威的专栏-CSDN博客
Handler如何保证MessageQueue并发访问安全?
我们可以发现MessageQueue其实是“生产者-消费者”模型,Handler不断地放入消息,Looper不断地取出,这就涉及到死锁问题。
如果Looper拿到锁,但是队列中没有消息,就会一直等待,而Handler需要把消息放进去,锁却被Looper拿着无法入队,这就造成了死锁。
Handler机制的解决方法是循环加锁。在MessageQueue的next方法中:
1
2
3
4
5
6
7
8
9
10
|
Message next() {
...
for (;;) {
...
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
...
}
}
}
|
Handler的阻塞唤醒机制是怎么回事?
Handler的阻塞唤醒机制是基于Linux的阻塞唤醒机制,主要的唤醒机制是epoll_wait函数。
这个机制也是类似于handler机制的模式。在本地创建一个文件描述符,然后需要等待的一方则监听这个文件描述符,唤醒的一方只需要修改这个文件,那么等待的一方就会收到文件从而打破唤醒。和Looper监听MessageQueue,Handler添加message是比较类似的。
源码茶舍之没有epoll就没有Handler - 掘金
能不能让一个Message加急被处理?/ 什么是Handler同步屏障?
可以 / 一种使得异步消息可以被更快处理的机制
注意,同步屏障不会自动移除,使用完成之后需要手动进行移除,不然会造成同步消息无法被处理。
消息屏障启动、关闭的方法:
1
2
3
4
5
6
7
8
9
10
11
|
// 反射插入消息屏障
MessageQueue queue = mHandler.getLooper().getQueue();
Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
method.setAccessible(true);
token = (int)method.invoke(queue);
// 反射移除消息屏障
MessageQueue queue = mHandler.getLooper().getQueue();
Method method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class);
method.setAccessible(true);
method.invoke(queue, token);
|
Handler之消息屏障你应该知道的_天亮了的博客-CSDN博客_handler消息屏障
一个Thread可以有几个Looper?几个Handler?
一个线程只能有一个Looper,可以有多个Handler,
在线程中我们需要调用Looper.perpare,
他会创建一个Looper并且将Looper保存在ThreadLocal中,
每个线程都有一个LocalThreadMap,会将Looper保存在对应线程中的LocalThreadMap,
key为ThreadLocal,value为Looper
Message可以如何创建?哪种效果更好,为什么
1
2
|
// 最常用的方式,通过Message的无参构造函数创建一个Message 对象
Message message = new Message();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// Message.obtain()及其系列的重载方法,我们先看看他的无参方法的源码实现
Message message = Message.obtain();
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
//直接从本地获取一个message对象
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
//没调用一次该方法,就将新的message对象加入Message池子,sPoolSize就减少一个
sPoolSize--;
return m;
}
}
return new Message();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// new Handler().obtainMessage()及其系列的重载方法,我们先看看他的无参方法的源码实现:
Message message = new Handler().obtainMessage();
public final Message obtainMessage(){
return Message.obtain(this);
}
// Message.obtain(this)方法具体 如下:
public static Message obtain(Handler h) {
//方法内部最终还是调用了obtain()方法,又回到,了方式2,这跟方式2创建Message对象是一样的,本质上没有区别。
Message m = obtain();
m.target = h;
return m;
}
|
Handler发送消息的 Delay 可靠吗?
答案是不靠谱的,引起不靠谱的原因有如下
- 发送的消息太多,Looper负载越高,任务越容易积压,进而导致卡顿
- 消息队列有一些消息处理非常耗时,导致后面的消息延时处理
- 大于Handler Looper的周期时基本可靠(例如主线程>50ms)
- 对于时间精确度要求较高,不要用handler的delay作为即时的依据
hread、Handler和HandlerThread关系何在?
Handler:在android中负责发送和处理消息,通过它可以实现其他支线线程与主线程之间的消息通讯。
Thread:Java进程中执行运算的最小单位,亦即执行处理机调度的基本单位。某一进程中一路单独运行的程序。
HandlerThread:一个继承自Thread的类HandlerThread,Android中没有对Java中的Thread进行任何封装,而是提供了一个继承自Thread的类HandlerThread类,这个类对Java的Thread做了很多便利的封装。
视频资料