- // Log的TAG
- private static final String TAG = "Looper";
-
- // 线程本地变量,保证了每个线程仅有唯一的Looper对象。
- @UnsupportedAppUsage
- static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
- // 主线程的Looper,由ActivityThread的main方法内调用Looper.prepareMainLooper()进行创建。
- @UnsupportedAppUsage
- private static Looper sMainLooper;
- // 消息分发状态的观察者,有分发开始、分发异常、分发结束。
- private static Observer sObserver;
-
- // 消息队列
- @UnsupportedAppUsage
- final MessageQueue mQueue;
- // 创建Looper的线程
- final Thread mThread;
- // 是否在loop中
- private boolean mInLoop;
-
- // 日志打印类
- @UnsupportedAppUsage
- private Printer mLogging;
- // Trace追踪标记
- private long mTraceTag;
-
- // 慢分发阈值时间,如果设置了这个参数,如果消息分发的时间超过这个值,则这个looper将显示一个警告日志。
- private long mSlowDispatchThresholdMs;
- // 慢交付阈值时间,如果设置了这个参数,如果消息执行(实际交付时刻dispatchStart - post时刻msg.when)的时间超过这个值,则这个looper将显示一个警告日志。
- private long mSlowDeliveryThresholdMs;
- // 是否慢交付检测,如果消息交付时间超过mSlowDeliveryThresholdMs,则为true。
- private boolean mSlowDeliveryDetected;
- 复制代码
说明:
Looper为什么需要持有MessageQueue,因为Looper轮询的时候需要知道轮询哪个MessageQueue。
想要使用Looper,首先要创建Looper,所以我们接下来看下它是如何被创建的。
- public static void prepare() {
- prepare(true);
- }
-
- private static void prepare(boolean quitAllowed) {
- if (sThreadLocal.get() != null) {
- // 调用线程已经有Looper了,再进行prepare,则抛出异常(每个线程只能创建一个Looper)。
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- // 创建Looper,并存入到sThreadLocal中。
- sThreadLocal.set(new Looper(quitAllowed));
- }
- 复制代码
prepare()方法,为静态方法,为调用线程创建一个Looper并存入到sThreadLocal中,以便后续获取此线程的Looper。quitAllowed参数传入为ture,以允许退出Looper。
说明:
- 一个线程,只能调用一次
prepare()、prepare(boolean)方法,否则抛出异常。- 一个线程,只能有一个
Looper。prepare()方法创建的Looper,允许退出。
接下来我们来看一下Looper构造方法。
- private Looper(boolean quitAllowed) {
- // 创建消息队列
- mQueue = new MessageQueue(quitAllowed);
- // 记录创建线程
- mThread = Thread.currentThread();
- }
- 复制代码
Looper构造方法,为私有方法,不能通过new创建,参数quitAllowed为是否允许退出,创建Looper会创建一个MessageQueue,并记录创建线程。
说明:
- 一个
Looper,只有一个MessageQueue,只有一个关联Thread。
- @Deprecated
- public static void prepareMainLooper() {
- // 准备Looper,如没有则进行创建,不允许退出。
- prepare(false);
- // 同步,保证线程安全。
- synchronized (Looper.class) {
- if (sMainLooper != null) {
- // 已经创建MainLooper了,再准备,则抛出异常。
- throw new IllegalStateException("The main Looper has already been prepared.");
- }
- // 获取调用线程的Looper,为MainLooper。
- sMainLooper = myLooper();
- }
- }
- 复制代码
prepareMainLooper()方法,为静态方法,为主线程准备Looper,并传入quitAllowed为false使其不允许退出,并将其标记为app的主looper。myLooper()相关介绍,看后面的-获取Looper。
已被声明为@Deprecated(弃用),因为app的主线程looper是由Android环境创建的(ActivityThread的main方法内调用Looper.prepareMainLooper()创建),因此永远不需要自己调用这个函数。
说明:
- 多个线程只能调用一次
prepareMainLooper()方法,否则抛出异常。- 主线程
Looper,是由Android环境创建的(ActivityThread的main方法),不需要我们关心。- 主线程
Looper,不允许退出。
- 创建-非主线程
Looper,只能通过Looper.prepare()方法创建,它是允许退出的。- 创建-主线程
Looper,它是通过Android环境创建(ActivityThread的main方法内调用Looper.prepareMainLooper()创建),它是不允许退出的。- 一个线程,一个
Looper,一个MessageQueue。
创建完Looper后,便可以获取Looper使用了,所以我们接下来看下它是如何被创建的。
- public static @Nullable Looper myLooper() {
- return sThreadLocal.get();
- }
- 复制代码
myLooper()方法,为静态方法,返回与当前线程关联的Looper对象。如果调用线程没有与Looper关联,则返回null。
- public static Looper getMainLooper() {
- synchronized (Looper.class) {
- return sMainLooper;
- }
- }
- 复制代码
getMainLooper()方法,为静态方法,返回主线程Looper对象。
- 获取
Looper,有两个方法:Looper.myLooper()、Looper.getMainLooper()。Looper.myLooper()方法,获取调用线程的Looper,有可能为null。Looper.getMainLooper()方法,获取主线程的Looper,不会为null。
当Looper创建好后,需要调用Looper.loop()方法,使其进入消息循环中,所以我们接下来看下它的Looper.loop()方法。
- public static void loop() {
- // 获取当前线程Looper对象
- final Looper me = myLooper();
- if (me == null) {
- // Looper对象为空,抛出异常(没有Looper; 这个线程上没有调用Looper.prepare())。
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- if (me.mInLoop) {
- // 已经在loop中,再调用loop(),则提示警告(再次loop将使队列中的消息在此消息完成之前被执行)。
- Slog.w(TAG, "Loop again would have the queued messages be executed"
- + " before this one completed.");
- }
- // 标记在loop中
- me.mInLoop = true;
-
- // 确保这个线程的标识是本地进程的标识,并跟踪标识token实际上是什么。
- Binder.clearCallingIdentity();
- final long ident = Binder.clearCallingIdentity();
-
- // 允许用系统道具(adb命令)覆盖一个阈值,例如:
- // 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);
- // 慢交付检测,恢复默认值。
- me.mSlowDeliveryDetected = false;
-
- // 死循环,遍历消息队列里的消息。
- for (;;) {
- // 调用loopOnce方法,进行处理。
- if (!loopOnce(me, ident, thresholdOverride)) {
- // loopOnce方法返回false,退出循环,结束loop。
- return;
- }
- }
- }
- 复制代码
loop()方法,为静态方法,内部执行死循环进行轮询,调用loopOnce()方法轮询单个消息。loopOnce()方法返回true,则继续轮询下一个消息;返回false,则退出循环,结束loop。
说明:
- 当前线程调用
Looper.loop()方法前,一定要为当前线程准备Looper(调用Looper.prepare()),否则抛出异常。
接下来,我们来看一下loopOnce()方法。
- private static boolean loopOnce(final Looper me,
- final long ident, final int thresholdOverride) {
- // 获取此Looper的消息队列里面的下个消息,有可能会阻塞线程。
- Message msg = me.mQueue.next();
- if (msg == null) {
- // 没有消息,表示消息队列正在退出(调用quit()方法),直接返回false以退出loop。
- return false;
- }
-
- // 获取日志打印类,这必须在一个局部变量中,以防UI事件设置logger。
- final Printer logging = me.mLogging;
- if (logging != null) {
- // 打印日志:Dispatching(分发中)
- logging.println(">>>>> Dispatching to " + msg.target + " "
- + msg.callback + ": " + msg.what);
- }
-
- // 获取Looper的全局观察者,使用变量,确保观察者在处理事务时不会更改。
- final Observer observer = sObserver;
-
- // 获取追踪标记
- final long traceTag = me.mTraceTag;
- // 获取消息分发阈值
- long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
- // 获取消息交付阈值
- long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
- if (thresholdOverride > 0) {
- // 有覆盖的(adb命令传入),则用覆盖的值。
- 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;
- // token,用于Observer回调间传递。
- Object token = null;
- if (observer != null) {
- // 获取token,通知Observer消息分发开始。
- token = observer.messageDispatchStarting();
- }
- // 设置当前线程的Uid
- long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
- try {
- // 通知Message的Handler进行分发消息(重要!!!)
- msg.target.dispatchMessage(msg);
- if (observer != null) {
- // 通知Observer消息分发结束
- observer.messageDispatched(token, msg);
- }
- // 记录分发结束时刻
- dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
- } catch (Exception exception) {
- if (observer != null) {
- // 通知Observer消息分发异常
- observer.dispatchingThrewException(token, msg, exception);
- }
- throw exception;
- } finally {
- // 恢复当前线程的Uid
- ThreadLocalWorkSource.restore(origWorkSource);
- if (traceTag != 0) {
- // 结束追踪
- Trace.traceEnd(traceTag);
- }
- }
- if (logSlowDelivery) {
- // 记录慢交付,如果(dispatchStart - msg.when)时间大于阈值,则进行Slog提示警告。
- if (me.mSlowDeliveryDetected) {
- if ((dispatchStart - msg.when) <= 10) {
- Slog.w(TAG, "Drained");
- me.mSlowDeliveryDetected = false;
- }
- } else {
- if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
- msg)) {
- me.mSlowDeliveryDetected = true;
- }
- }
- }
- if (logSlowDispatch) {
- // 记录慢分发,如果(dispatchEnd - dispatchStart)时间大于阈值,则进行Slog提示警告。
- showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
- }
-
- if (logging != null) {
- // 打印日志:Finished(已完成)
- 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();
-
- return true;
- }
- 复制代码
loopOnce()方法,为静态方法,为轮询单个消息。它会先获取消息队列的下个Message,然后调用Message的目标Handler进行分发。
说明:
- 当
Looper的MessageQueue里面没有消息时,则线程进入阻塞,释放CPU,它不会因为没有数据而停止loop(),所以如果此Looper不使用了,一定要调用quit()来结束循环,它会使MessageQueue的next()方法返回null,从而结束循环。- 可通过调用
looper.setMessageLogging()给指定Looper设置日志打印者,以监听消息状态,>>>>> Dispatching to为分发开始,<<<<< Finished to为分发结束,可判断时间差来判断分发是否超时。
- 轮询
Looper前,调用线程一定要有Looper(需调用Looper.prepare()),否则抛出异常。- 非主线程
Looper,如果不使用了,一定要调用quit()来结束循环。- 当
MessageQueue里面没有消息时,则线程进入阻塞,释放CPU,它不会因为没有数据而停止loop(),所以如果此Looper不使用了,一定要调用quit()来结束循环。
Looper不使用了,便需要退出,所以我们接下来看下它是如何退出的。
- public void quit() {
- mQueue.quit(false);
- }
- 复制代码
quit()方法,为不安全的退出Looper。
说明:
- 此方法,导致
loop()方法,在不处理消息队列中的任何消息的情况下终止,因为它会删除消息队列中所有消息。- 在
looper被请求退出后,任何向队列发送消息的尝试都将失败。例如,Handler.sendMessage(Message)方法将返回false。- 此方法,可能是不安全的,因为在
looper终止之前可能无法传递一些消息。考虑使用quitSafely,以确保所有等待的工作都以有序的方式完成。
- public void quitSafely() {
- mQueue.quit(true);
- }
- 复制代码
quitSafely()方法,为安全的退出Looper。
说明:
- 此方法,导致
loop()方法,在处理消息队列中的已经到期的所有消息,不处理消息队列中的未来(当前时刻以后)到期的所有消息的情况下终止,因为它会删除消息队列中所有未来的消息。
- 退出
Looper,有两个方法:quit()不安全退出、quitSafely()安全退出。quit()方法,在不处理消息队列中的任何消息的情况下终止(此方法,可能是不安全的,因为在looper终止之前可能无法传递一些消息)。quitSafely()方法,在处理消息队列中的已经到期的所有消息,不处理消息队列中的未来(当前时刻以后)到期的所有消息的情况下终止。- 在
looper被请求退出后,任何向队列发送消息的尝试都将失败。例如,Handler.sendMessage(Message)方法将返回false。
Looper的mLogging、mThread、mQueue属性对外提供了set、get方法,我们接下来看下它们的实现。
- public void setMessageLogging(@Nullable Printer printer) {
- mLogging = printer;
- }
- 复制代码
设置此Looper处理消息时控制消息的日志记录。如果启用,日志消息将在每次消息分发的开始和结束时写到printer,标识目标Handler和消息内容。
- public @NonNull Thread getThread() {
- return mThread;
- }
- 复制代码
获取与此looper关联的线程。
- public @NonNull MessageQueue getQueue() {
- return mQueue;
- }
- 复制代码
获取此looper的消息队列。
- public static @NonNull MessageQueue myQueue() {
- return myLooper().mQueue;
- }
- 复制代码
获取与当前线程关联的MessageQueue对象。此线程必须有Looper,否则将抛出NullPointerException。
- public boolean isCurrentThread() {
- return Thread.currentThread() == mThread;
- }
- 复制代码
判断调用线程是否是该looper的线程,如果是,则返回true。
- public void dump(@NonNull Printer pw, @NonNull String prefix) {
- pw.println(prefix + toString());
- mQueue.dump(pw, prefix + " ", null);
- }
- 复制代码
转储looper的状态,以进行调试,调用MessageQueue的dump()方法。