版权声明:本文为博主原创文章,转载请注明出处,如有问题,欢迎指正,谢谢。 https://blog.csdn.net/qq_33404903/article/details/89461197
Straightforward
/**
* 用来为一个线程运行消息循环的类。默认的线程本身是没有与之相关联的消息循环的;
* 要创建一个的话,在将要运行循环的线程中调用 {@link #prepare},然后调用
* {@link #loop} 来让它处理消息,直到循环停止。
*
*
* <p>与消息循环的大多数交互都是通过 {@link Handler} 类进行的。
*
* <p>这是一个典型的 Looper 线程实现的例子,
* 使用 {@link #prepare} 和 {@link #loop} 的分离创建一个初始的 Handler
* 来和 Looper 交流。
*
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // 在此处理传入的消息
* }
* };
*
* Looper.loop();
* }
* }</pre>
*/
public final class Looper {
/*
* API实现说明:
*
* 该类包含基于 MessageQueue 设置和管理事件循环所需的代码。
* 影响队列状态的 APIs 应该在 MessageQueue 和 Handler 上定义,而不是在 Looper 本身上定义。
* 例如,空闲的 Handlers 和同步障碍被定义在准备线程的队列上,循环和退出则定义在 looper 上。
*/
private static final String TAG = "Looper";
// sThreadLocal.get() 将返回 null,除非你调用了 prepare()。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // 由 Looper.class 守护
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
private long mTraceTag;
/**
* 若设置此值,如果消息调度花费的时间比它长,looper 将显示警告日志。
*/
private long mSlowDispatchThresholdMs;
/**
* 若设置,如果消息传递(实际传递时间 - 到达时间)花费的时间超过此时间,looper 将显示警告日志
*/
private long mSlowDeliveryThresholdMs;
/**
* 将当前线程初始化为一个 looper。
* 这使您有机会在实际启动循环之前创建引用此 looper 的 handlers。
* 一定要在调用这个方法之后调用 {@link #loop()},并通过调用 {@link #quit()} 结束它。
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* 将当前线程初始化为 looper,将其标记为应用程序的 main looper。
* 应用程序的 main looper 是由 Android 环境创建的,因此您永远不需要自己调用此函数。
* 另见:{@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* 返回应用程序的 main looper,它位于应用程序的主线程中。
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
/**
* 在此线程中运行消息队列。务必调用{@link #quit()} 来结束循环。
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// 确保此线程的标识是本地进程的标识,并跟踪该标识标记的实际内容。
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 允许使用系统属性覆盖阈值。
// 例如,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(); // 可能会阻塞
if (msg == null) {
// 没有消息意味着消息队列正在退出。
return;
}
// 这必须在局部变量中,以防 UI 事件设置 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)) {
// 一旦我们写了一个慢速传递 log,请在队列耗尽之前禁止。
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// 确保在调度过程中,线程的标识未损坏。
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;
}
// 对于慢速传递,当前消息并不重要,但无论如何都要记录它。
Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
+ Thread.currentThread().getName() + " h="
+ msg.target.getClass().getName() + " c=" + msg.callback + " m=" + msg.what);
return true;
}
/**
* 返回与当前线程关联的 Looper 对象。
* 如果调用线程未与 Looper 关联,则返回 null。
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
/**
* 必须从运行 Looper 的线程调用它,否则将抛出 NullPointerException。
*/
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
/**
* 如果当前线程是此 looper 的线程,则返回 true。
*/
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
/**
* 控制此 Looper 处理消息时的日志记录。
* 如果启用,将在每个消息调度的开始和结束时将日志消息写入 printer,以标识目标 Handler 和消息内容。
*
* @param printer 将接收日志消息的 Printer 对象,或者为 null 以禁用消息日志记录。
*/
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
/** {@hide} */
public void setTraceTag(long traceTag) {
mTraceTag = traceTag;
}
/**
* 设置慢速调度/传递日志的阈值。
* {@hide}
*/
public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs) {
mSlowDispatchThresholdMs = slowDispatchThresholdMs;
mSlowDeliveryThresholdMs = slowDeliveryThresholdMs;
}
/**
* 退出 looper
* <p>
* 将造成 {@link #loop} 方法终止,而不处理消息队列中的任何其它消息。
* </p><p>
* 请求 looper 退出后,任何向队列发送消息的尝试都将失败。
* 例如,{@link Handler#sendMessage(Message)} 方法将返回 false。
* </p><p class="note">
* 使用此方法可能不安全,因为在 looper 终止之前可能无法传递某些消息。
* 请考虑使用 {@link #quitSafely} 来确保所有待处理的工作都以有序的方式完成。
* </p>
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
/**
* 安全地退出 looper。
* <p>
* 一旦处理了已经发送的消息队列中的所有剩余消息,就会造成 {@link #loop} 方法终止。
* 但是,在循环终止之前,将不会发送具有未来到期时间的待处理延迟消息。
* </p><p>
* 请求 looper 退出后,向队列发送消息的任何尝试都将失败。
* 例如,{@link Handler#sendMessage(Message)} 方法将返回 false。
* </p>
*/
public void quitSafely() {
mQueue.quit(true);
}
/**
* 获取与此 Looper 关联的线程。
*
* @return looper 的线程。
*/
public @NonNull Thread getThread() {
return mThread;
}
/**
* 获取该 looper 的消息队列。
*
* @return 该 looper 的消息队列。
*/
public @NonNull MessageQueue getQueue() {
return mQueue;
}
/**
* 转储 looper 的状态以进行调试。
*
* @param pw 用于接收转储内容的 printer。
* @param prefix 加到打印的每一行之前的前缀。
*/
public void dump(@NonNull Printer pw, @NonNull String prefix) {
pw.println(prefix + toString());
mQueue.dump(pw, prefix + " ", null);
}
/**
* 转储 looper 的状态以进行调试。
*
* @param pw 用于接收转储内容的 printer。
* @param prefix 加到打印的每一行之前的前缀。
* @param handler 仅转储此 Handler 的消息。
* @hide
*/
public void dump(@NonNull Printer pw, @NonNull String prefix, Handler handler) {
pw.println(prefix + toString());
mQueue.dump(pw, prefix + " ", handler);
}
/** @hide */
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long looperToken = proto.start(fieldId);
proto.write(LooperProto.THREAD_NAME, mThread.getName());
proto.write(LooperProto.THREAD_ID, mThread.getId());
mQueue.writeToProto(proto, LooperProto.QUEUE);
proto.end(looperToken);
}
@Override
public String toString() {
return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
+ ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
}
}