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
2. MessageQueue
3. Looper
Looper 作为 MessageQueue 的载体,负责组织 Handler 、 Thread 、 MessageQueue 三者之间的关系。
3.1 继承关系
// 无继承、无实现
public final class Looper {
3.2 变量
// 静态变量,存储所有的、不同线程的Looper实例(如果你在该线程没有调用过Looper.prepare(),将会返回null)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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 的资源释放。
网友评论