Handler 基础了解
1. Handler 是干嘛的
说白了,Handler 就是 Android 提供的 线程之间交互 的一种方式,并在系统层面同样大量使用。
2. Handler 的工作流程是怎样的
当每个线程创建时,可选择 为该线程创建自己的循环队列 (Looper + MessageQueue) ,当 [线程B] 想发送消息给 [线程A] 时,只需要在 [线程B] 把消息推送到 [线程A] 的 MessageQueue 中,[线程A] 就可以用 Looper 提取 MessageQueue 中的 Message 了
3. 系统有哪些地方使用了 Handler
- CountDownTimer
- Messenger
- AsyncTask
- IntentService
- View
- LocalBroadcast
- 等
4. Handler 机制所使用的主要类
- Message
- MessageQueue
- Looper
- Handler
5. Handler 的基础使用方法
// 1.在需要接受数据的线程创建Handler
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
// 4.这里接收发送的数据
}
}
// 2.在需要发送数据的线程创建Message
Message message = Message.obtain();__
// 3.调用接收线程的handler发送消息
handler.sendMessage(message);
主要类源码分析
1. Message
Handler机制源码之路(一)Message篇
2. MessageQueue
Handler机制源码之路(二)MessageQueue篇
3. Looper
Looper 作为 MessageQueue 的载体,负责组织 Handler 、 Thread 、 MessageQueue 三者之间的关系。
3.1 继承关系
// 无继承、无实现
public final class Looper {
3.2 变量
// 静态变量,存储所有的、不同线程的Looper实例(如果你在该线程没有调用过Looper.prepare(),将会返回null)
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;// 消息队列
final Thread mThread;// 当前线程
private Printer mLogging;
private long mTraceTag;
/**
* 如果设置,如果消息发送时间超过此时间,则Looper将显示警告日志。
*/
private long mSlowDispatchThresholdMs;
/**
* 如果设置了,则如果消息传递(实际传递时间-发布时间)比此时间长,则Looper将显示警告日志。
*/
private long mSlowDeliveryThresholdMs;
3.3 Looper 的 prepare() 方法
该方法作用是与 Looper 所在线程进行绑定,每个线程只允许绑定一个 Looper,同时,每个 Looper 有一个 MessageQueue 的成员变量,故此可以保证,一个线程只有一个消息队列。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 判断是否创建过looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* 为当前线程创建Looper,使它成为应用的主Looper。该方法已被系统调用过,所以你不必在你自己的应用中再次调用
* /
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
通过查看源码调用可以看到,在 ActivityThread 的 main 方法中,调用了 Looper.prepareMainLooper()
3.3.1 关于 ThreadLocal
可以看到,在 prepare() 方法中,是通过 sThreadLocal.get() 来判断当前线程是否已经创建Looper,那么,prepare() 作为一个静态方法,为什么可以用这种方法来判断呢,ThreadLocal 中是如何实现的?
具体源码设计到Java部分,就不一一贴出,只说一下原理是这样的:
每一个 Thread 有一个成员变量 threadLocals,类型是 ThreadLocal.ThreadLocalMap, 所以每一个 Thread 都可以有自己存储变量的空间。
当调用 sThreadLocal.get() 就是从 Thread.currentThread() 获取当前线程, 再从当前线程的 threadLocals 中取数据,从而保证了所取的值仅为当前线程所保存的值。
当调用 sThreadLocal.set(new Looper()) 时,先从Thread.currentThread() 获取当前线程,然后将 Looper 存入到当前线程的 threadLocals 中。
Looper实例 获取所在线程的方法为内部变量 mThread ,当前线程获取 Looper实例 的方法为 Looper.myLooper() ,获取 主线程Looper 的方法为 getMainLooper()。
3.4 Looper 的 loop() 方法
loop()方法的核心逻辑只有几行代码,其他代码主要是使用trace来检测系统流畅度的。
public static void loop() {
final Looper me = myLooper();
if (me == null) {// 没有创建Looper,不允许开启循环
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
......
for (;;) {
// 死循环从MessageQueue中获取消息,如果返回消息为空,则跳出循环,Looper结束
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
try {
// 使用Message中存的Handler分发消息
msg.target.dispatchMessage(msg);
} finally {}
......
// 回收消息
msg.recycleUnchecked();
}
}
其他代码将在 3.7 Looper 的 日志插入机制 中说明
3.5 Looper 的 quit() 方法
Looper 的退出方法, 会调用 MessageQueue 的 quit() 方法,在 MessageQueue 中将消息队列的 mQuitting 置为 true , 从而使 MessageQueue 的 next() 方法返回 null, 这样可以引起 Looper 的 loop() 方法判空后跳出循环结束。
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
3.6 Looper 的 get() 方法
- myLooper() 方法
// 静态方法,用于获取当前线程的Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
- myQueue() 方法
// 静态方法,用于获取当前线程的消息队列
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
- isCurrentThread() 方法
// 判断当前线程是否为Looper绑定的线程
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
- getThread() 方法
// 获取实例的线程
public @NonNull Thread getThread() {
return mThread;
}
- getQueue() 方法
// 获取实例的消息队列
public @NonNull MessageQueue getQueue() {
return mQueue;
}
3.7 Looper 的 日志插入机制
我们知道,UI卡顿是一个非常不好的用户体验,那么如何解决UI卡顿,这是一个很深的问题,这里不具体讨论。那么如何检测UI卡顿呢?其实原理就在于这个Looper的日志插入机制。
先来看几个方法:
- setMessageLogging() 方法
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
- setTraceTag() 方法
public void setTraceTag(long traceTag) {
mTraceTag = traceTag;
}
- setSlowLogThresholdMs() 方法
public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs) {
mSlowDispatchThresholdMs = slowDispatchThresholdMs;
mSlowDeliveryThresholdMs = slowDeliveryThresholdMs;
}
上面方法表示,我们可以在 Looper 中设置日志输出信息的配置,当 Looper 分发消息的时候,会根据配置来回调这些配置好的回调。具体实现在 Looper.loop() 方法中。
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
- 日志显示方法
private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
String what, Message msg) {
final long actualTime = measureEnd - measureStart;
if (actualTime < threshold) {
return false;
}
// For slow delivery, the current message isn't really important, but log it anyway.
Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
+ Thread.currentThread().getName() + " h="
+ msg.target.getClass().getName() + " c=" + msg.callback + " m=" + msg.what);
return true;
}
3.8 总结
每个线程只允许有一个Looper,Looper 的创建方法为 prepare() ,对应的退出方法为 quit() ,
一旦 prepare() 以后,实际上 MessageQueue 在 native 初始化,而 loop() 的作用则是从 MessageQueue 中取消息,如果不执行 loop(),则永远无法在 handleMessage() 中接收到数据,但 MessageQueue 已经初始化。只有调用了 quit()。MessageQueue 才将 native 的资源释放。
4. Handler
Handler机制源码之路(四)Handler篇
Handler应用类拓展阅读
1.CountDownTimer
Handler机制应用之CountDownTimer篇