难点:html
next()
方法,enqueueMessage()
方法,由于它们与 Native 层的 Looper 和 MQ 关联。重点:java
Handler 机制中有 4 个主要的对象:Handler、Message、MessageQueue 和 Looper. Handler 负责消息的发送和处理;Message 是消息对象,相似于链表的一个结点;MessageQueue 是消息队列,用于存放消息对象的数据结构;Looper 是消息队列的处理者(用于轮询消息队列的消息对象,取出后回调 handler 的 dispatchMessage()
进行消息的分发,dispatchMessage()
方法会回调 handleMessage()
方法把消息传入,由 Handler 的实现类来处理。)git
当咱们在某个线程当中调用 new Handler()
的时候会使用当前线程的 Looper 建立 Handler. 当前线程的 Looper 存在于线程局部变量 ThreadLocal 中。在使用 Handler 以前咱们须要先调用 Looper.prepare()
方法实例化当前线程的 Looper,并将其放置到当前线程的线程局部变量中(只放一次,之后会先从 TL 中获取再使用,此时会调用 Looper 的构造方法,并在构造方法中初始化 MQ),而后调用 Looper.loop()
开启消息循环。主线程也是同样,只是主线程的 Looper 在 ActivityThread 的 main()
方法中被实例化。咱们可使用 Looper.getMainLooper()
方法来获取主线程的 Looper,并使用它来建立 Handler,这样咱们就能够在任何线程中向主线程发送消息了。github
Looper.prepare(); // 内部会调用 Looper 的 new 方法实例化 Looper 并将其放进 TL
new Handler().post(() -> /* do something */);
Looper.loop();
复制代码
当实例化 Looper 的时候会同时实例化一个 MessageQueue,而 MessageQueue 同时又会调用 Native 层的方法在 Native 层实例化一个 MessageQueue 还有 Looper. Java 层的 Looper 和 Native 层的 Looper 之间使用 epoll 进行通讯。当调用 Looper 的 loop()
方法的时候会启动一个循环来对消息进行处理。Java 层的 MQ 中没有消息的时候,Native 层的 Looper 会使其进入睡眠状态,当有消息到来的时候再将其唤醒起来处理消息,以节省 CPU.面试
在 Looper 的 loop()
中开启无限循环为何不会致使主线程 ANR 呢?这是由于 Android 系统自己就是基于消息机制的,所谓的消息就是指发送到主线程当中的消息。之因此产生 ANR 并非由于主线程当中的任务无限循环,而是由于无限循环致使其余的事件得不处处理。安全
《Android 消息机制:Handler、MessageQueue 和 Looper》数据结构
handler内存泄漏及解决办法:若是 Handler 不是静态内部类,Handler 会持有 Activity 的匿名引用。当 Activity 要被回收时,由于 Handler 在作耗时操做没有被释放,Handler Activity 的引用不能被释放致使 Activity 没有被回收停留在内存中形成内存泄露。多线程
解决方法是:1). 将 Handler 设为静态内部类;2). 使 Handler 持有 Activity 的弱引用;3). 在 Activity 生命周期 onDestroy()
中调用 Handler.removeCallback()
方法。异步
Android 中的控件不是线程安全的,之因此这样设计是为了:1).设计成同步的能够简化使用的复杂度;2).能够提高控件的性能(异步加锁在非多线程环境是额外的开销)。oop
post()
方法时 Handler 所在的线程)post()
方法所在的线程由 Looper 所在线程决定的;最终逻辑是在 Looper.loop()
方法中,从 MQ 中拿出 Message,而且执行其逻辑。这是在 Looper 中执行的。所以由 Looper 所在线程决定。
不论你调用 send()
类型的方法仍是 post()
类型的方法,最终都会调用到 sendMessageAtTime()
方法。post()
和 postDelay()
的区别在于,前者使用当前时间,后者使用当前时间+delay 的时间来决定消息触发的时间。最终方法的参数都将被包装成一个 Message 对象加入到 Handler 对应的 Looper 的 MQ 中被执行。
Looper 和 Handler 不须要再一个线程中,默认的状况下会从 TL 中取当前线程对应的 Looper,但咱们能够经过显式地指定一个 Looper 的方式来建立 Handler. 好比,当咱们想要在子线程中发送消息到主线程中,那么咱们能够
Handler handler = new Handler(Looper.getMainLooper());
复制代码
用户层面发送的都是同步消息,不能发送异步消息;异步消息只能由系统发送。
调用 MessageQueue.next()
方法的时候会调用 Native 层的 nativePollOnce()
方法进行精准时间的阻塞。在 Native 层,将进入 pullInner()
方法,使用 epoll_wait
阻塞等待以读取管道的通知。若是没有从 Native 层获得消息,那么这个方法就不会返回。此时主线程会释放 CPU 资源进入休眠状态。
当咱们加入消息的时候,会调用 MessageQueue.enqueueMessage()
方法,添加完 Message 后,若是消息队列被阻塞,则会调用 Native 层的 nativeWake()
方法去唤醒。它经过向管道中写入一个消息,结束上述阻塞,触发上面提到的 nativePollOnce()
方法返回,好让加入的 Message 获得分发处理。
MessageQueue.enqueueMessage()
使用 synchronized 代码块去进行同步。
资料:Android 中的 Handler 的 Native 层研究
dispatchMessage()
分发消息的处理流程?使用 Handler 的时候咱们会覆写 Handler 的 handleMessage()
方法。当咱们调用该 Handler 的 send()
或者 post()
发送一个消息的时候,发送的信息会被包装成 Message,而且将该 Message 的 target 指向当前 Handler,这个消息会被放进 Looper 的 MQ 中。而后在 Looper 的循环中,取出这个 Message,并调用它的 target Handler,也就是咱们定义的 Handler 的 dispatchMessage()
方法处理消息,此时会调用到 Handler 的 handleMessage()
方法处理消息,并回调 Callback.
当 Handler 在消息队列中被执行的时候会直接调用 Handler 的 dispatchMessage()
方法回调 Callback.
Looper.myLooper()
是如何获取到当前线程的 Looper 的?从 TL 中获取
是单链表,不是队列
quit()
和 quitSafely()
的本质就是让消息队列的 next()
返回 null,以此来退出Looper.loop()
。
quit()
调用后直接终止 Looper,不在处理任何 Message,全部尝试把 Message 放进消息队列的操做都会失败,好比 Handler.sendMessage()
会返回 false,可是存在不安全性,由于有可能有 Message 还在消息队列中没来的及处理就终止 Looper 了。
quitSafely()
调用后会在全部消息都处理后再终止 Looper,全部尝试把 Message 放进消息队列的操做也都会失败。
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
void quit(boolean safe) {
if (!mQuitAllowed) throw new IllegalStateException("Main thread not allowed to quit.");
synchronized (this) {
if (mQuitting) return;
mQuitting = true;
if (safe) removeAllFutureMessagesLocked(); // 把全部延迟消息清除
else removeAllMessagesLocked(); // 直接把消息队列里面的消息清空
nativeWake(mPtr);
}
}
复制代码
1).next()
方法返回的 msg == null;2).线程意外终止。
next()
方法;msg.target.dispatchMessage(msg)
进行消息分发。Looper.loop()
方法执行时,若是内部的 myLooper()
获取不到Looper会出现什么结果?异常
经过保证只有一个 Looper 来保证只有以一个 MQ. 在一个线程中使用 Handler 以前须要使用 Looper.prepare()
建立 Looper,它会从 TL 中获取,若是发现 TL 中已经存在 Looper,就抛异常。
根据消息的分发机制,Looper 不会区分 Handler,每一个 Handler 会被添加到 Message 的 target 字段上面,Looper 经过调用 Message.target.handleMessage()
来让 Handler 处理消息。
关注做者,及时获取更多高级面试题解 :)
Android 高级面试系列文章,关注做者及时获取更多面试资料,
本系列以及其余系列的文章均维护在 Github 上面:Github / Android-notes,欢迎 Star & Fork. 若是你喜欢这篇文章,愿意支持做者的工做,请为这篇文章点个赞👍!