源码分析 Handler 机制(四) —— Looper

本文基于 SDK-33

在前面的文章中,Handler 创建了,消息创建了,也通过调用 MessageQueue 的 enqueueMessage 方法将消息插入到了 MessageQueue 中了,也知道 MessageQueue 对象的 next() 方法可以将消息从 MessageQueue 中取出,这篇文章分析一下 next 方法的调用处:Looper。

Looper 的创建

在创建 Handler 对象时,需要传入一个 Looper 对象,先看 Looper 的构造方法:

    final MessageQueue mQueue;
    final Thread mThread;   

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

可以看到,Looper 的构造方法中可以看出,Looper 是和当前线程、MessageQueue 绑定的,在创建 Handler 的时候,Looper 作为参数,也和 Handler 绑定到了一起。

Looper 的构造方法是 private 的,无法直接通过 new 来创建,官方提供了 prepare 方法来创建 Looper 对象:

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    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 维护了一个 ThreadLocal 对象,可以保证线程和 Looper 的一一对应,如果当前线程已经绑定了 Looper,则会抛出异常。

Looper 源码中还有一个已经弃用的 prepareMainLooper 方法:

    @Deprecated
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

该方法用于为主线程创建 Looper,但该方法注释中明确写着:

The main looper for your application is created by the Android environment, so you should never need to call this function yourself.

系统已经自己创建了 UI 线程的 Looper 并与主线程绑定,无须开发者自行创建。

获取 Looper

Looper 对象提供了如下方法可以获取到已经创建的 Looper 对象:

    //获取主线程绑定的 Looper
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

    //获取当前线程绑定的 Looper,如果当前线程(子线程)没有绑定 Looper,则返回null
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

Looper 从 MessageQueue 中获取消息

Looper 维护了一个 MessageQueue,Handler 将 MessageQueue 发送到这里,那么消息是怎么取出的呢?Looper 提供了 loop 方法:

    public static void loop() {
        final Looper me = myLooper();//获取到当前线程绑定的 Looper
        if (me == null) {//如果线程没绑定 Looper,则会抛出异常
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        if (me.mInLoop) {//如果该线程已经调用过 loop 方法,则会
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;//标记已经调用 loop

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        // native 层方法
        // 这个方法的作用是 重置当前进程里的Identity, 将当前的进程中保存的(PID,UID)换成当前的PID UID, 只有这样在校验权限的时候才可以有权限调用本进程的方法
        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'
        // 允许通过 adb 命令设置一个阈值
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);
        //设置不进行传输超时警告
        me.mSlowDeliveryDetected = false;
        //死循环
        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }

重点在 loopOnce 方法这里:

    private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        //调取 MessageQueue 方法获取消息,这里有可能会阻塞
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {//如果没有消息,表示 MessageQueue 可能正在退出
            // No message indicates that the message queue is quitting.
            return false;//返回 false。退出 loop
        }

        // This must be in a local variable, in case a UI event sets the logger
        //打印 log 相关设置
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what);
        }
        // Make sure the observer won't change while processing a transaction.
        // 设置 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;
        Object token = null;
        if (observer != null) {
            //在分发消息之前,获取token
            token = observer.messageDispatchStarting();
        }
        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        try {
            //【【重要】】消息分发到 Handler 的 dispatchMessage 中执行
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                //分发结束后调用
                observer.messageDispatched(token, msg);
            }
            //获取分发结束时间
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                //通知分发异常
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            //结束追踪
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //如果是慢交付
        if (logSlowDelivery) {
            //如果设置了交付超时警告
            if (me.mSlowDeliveryDetected) {
                //如果 交付时间-预定执行时间 <= 10 毫秒,则关闭交付超时警告
                if ((dispatchStart - msg.when) <= 10) {
                    Slog.w(TAG, "Drained");
                    me.mSlowDeliveryDetected = false;
                }
            } else {
                //如果超出 10 毫秒,则开启交付超时警告
                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                        msg)) {
                    // Once we write a slow delivery log, suppress until the queue drains.
                    me.mSlowDeliveryDetected = 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();

        return true;
    }

其他方法:

获取 Looper 对应的 MessageQueue:

    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

对比线程

当前线程是该 Looper 绑定的线程,则返回 true。

    public boolean isCurrentThread() {
        return Thread.currentThread() == mThread;
    }

退出

实际上调用的是 MessageQueue 的退出方法

    public void quit() {
        mQueue.quit(false);
    }

    public void quitSafely() {
        mQueue.quit(true);
    }
Copyright© 2020-2022 li-xyz 冀ICP备2022001112号-1