Handler机制源码之路(三)Looper篇

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 的载体,负责组织 HandlerThreadMessageQueue 三者之间的关系。

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 的退出方法, 会调用 MessageQueuequit() 方法,在 MessageQueue 中将消息队列的 mQuitting 置为 true , 从而使 MessageQueuenext() 方法返回 null, 这样可以引起 Looperloop() 方法判空后跳出循环结束。

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 总结

每个线程只允许有一个LooperLooper 的创建方法为 prepare() ,对应的退出方法为 quit() ,
一旦 prepare() 以后,实际上 MessageQueuenative 初始化,而 loop() 的作用则是从 MessageQueue 中取消息,如果不执行 loop(),则永远无法在 handleMessage() 中接收到数据,但 MessageQueue 已经初始化。只有调用了 quit()MessageQueue 才将 native 的资源释放。

4. Handler

Handler机制源码之路(四)Handler篇

Handler应用类拓展阅读

1.CountDownTimer

Handler机制应用之CountDownTimer篇

你可能感兴趣的:(Handler机制源码之路(三)Looper篇)