Matrix源码分析(一)之 LooperMonitor 工作原理

90 阅读4分钟

在讲 LooperMonitor 之前,我们先来看看如何在Looper 中添加日志打印,

先来看看Looper 的部分源码

public final class Looper {
    
    //设置打印的Printer
    public void setMessageLogging(@Nullable Printer printer) {
        mLogging = printer;
    }
    
    
    // 开启循环,处理消息
    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;

        // 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;
            // 开始处理消息时 如果有 logging 打印 >>>>
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

            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;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                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 (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);
            }
            结束处理消息时 如果有 logging 打印 <<<<<
            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();
        }
    }
}

可以看到在Looper 中有一个 setMessageLogging 方法,这个方法是整个Looper单例的,在Looper 处理消息的开始和结束时会调用这个 mLogging 打印 >>> 与 <<< , 在没有看 Matrix 源码之前我们先提出两个问题,

1: 由于 setMessageLogging 是 Looper 的静态方法 Matrix 是如何保证自己添加的进去的 Printer 不被别人所替换,又同时保证其他给 setMessageLogging 赋值进去的 Printer 还能继续打印的,

2:在接收到了 Printer 的消息 ,Matrix 如何来判断这个消息是开始消息还是结束消息的,这对于整个 Matrix 架构至关重要

带着这两个问题,我们开始分析 LooperMonitor

先看他的构造方法

private LooperMonitor(Looper looper) {
    Objects.requireNonNull(looper);
    this.looper = looper;
    resetPrinter();
    addIdleHandler(looper);
}

在构造方法中保存了looper ,同时调用了 resetPrinter 方法, 并且向looper 的 MessageQueue 中添加了一个自身的 IdleCallback ,我们先来看看 resetPrinter 这个方法

private synchronized void resetPrinter() {
    Printer originPrinter = null;
    try {
        if (!isReflectLoggingError) {
            originPrinter = ReflectUtils.get(looper.getClass(), "mLogging", looper);
            if (originPrinter == printer && null != printer) {
                return;
            }
            // Fix issues that printer loaded by different classloader
            if (originPrinter != null && printer != null) {
                if (originPrinter.getClass().getName().equals(printer.getClass().getName())) {
                    MatrixLog.w(TAG, "LooperPrinter might be loaded by different classloader"
                            + ", my = " + printer.getClass().getClassLoader()
                            + ", other = " + originPrinter.getClass().getClassLoader());
                    return;
                }
            }
        }
    } catch (Exception e) {
        isReflectLoggingError = true;
        Log.e(TAG, "[resetPrinter] %s", e);
    }

    if (null != printer) {
        MatrixLog.w(TAG, "maybe thread:%s printer[%s] was replace other[%s]!",
                looper.getThread().getName(), printer, originPrinter);
    }
    looper.setMessageLogging(printer = new LooperPrinter(originPrinter));
    if (null != originPrinter) {
        MatrixLog.i(TAG, "reset printer, originPrinter[%s] in %s", originPrinter, looper.getThread().getName());
    }
}

Matrix 使用了一个非常非常巧妙的方法,那就是反射Looper内的 mLogging ,如果mLogging 存在,并且不是我设置的,那么就会让自己的 LooperPrinter 包裹这个 mLogging ,在 LooperPrinter打印过程中先使用它持有Printer 打印,再来处理自己的逻辑,

再来看看 IdleCallback都干了啥

@Override
public boolean queueIdle() {
    if (SystemClock.uptimeMillis() - lastCheckPrinterTime >= CHECK_TIME) {
        resetPrinter();
        lastCheckPrinterTime = SystemClock.uptimeMillis();
    }
    return true;
}

每1分钟调用一次 重置 resetPrinter ,来保证自身的正确运转, 看完这这里我们就将第一个问题分析清楚了,

关于第二个问题,再来看看LooperPrinter 这个类

class LooperPrinter implements Printer {
    public Printer origin;
    boolean isHasChecked = false;
    boolean isValid = false;

    LooperPrinter(Printer printer) {
        this.origin = printer;
    }

    @Override
    public void println(String x) {
        if (null != origin) {
            origin.println(x);
            if (origin == this) {
                throw new RuntimeException(TAG + " origin == this");
            }
        }

        if (!isHasChecked) {
            isValid = x.charAt(0) == '>' || x.charAt(0) == '<';
            isHasChecked = true;
            if (!isValid) {
                MatrixLog.e(TAG, "[println] Printer is inValid! x:%s", x);
            }
        }

        if (isValid) {
            dispatch(x.charAt(0) == '>', x);
        }

    }
}

在他的println 方法内,调用了一个 dispatch 方法,这个方法第一个参数就是boolean 类型的是否是开始的标识,他使用的是 x是否是 '>'开头的, 剩下的事情就比较简单了,分发开始和结束事件 ,问题二我们也解释清楚了

private void dispatch(boolean isBegin, String log) {
    if (isBegin) {
        if (historyMsgRecorder) {
            messageStartTime = System.currentTimeMillis();
            latestMsgLog = log;
            recentMCount++;
        }
        synchronized (oldListeners) {
            for (LooperDispatchListener listener : oldListeners) {
                if (listener.isValid()) {
                    listener.onDispatchStart(log);
                }
            }
        }
        synchronized (listeners) {
            for (DispatchListenerWrapper listener : listeners.values()) {
                if (listener.isValid()) {
                    listener.onDispatchBegin(log);
                }
            }
        }
    } else {
        if (historyMsgRecorder) {
            recordMsg(log, System.currentTimeMillis() - messageStartTime);
        }
        synchronized (oldListeners) {
            for (LooperDispatchListener listener : oldListeners) {
                if (listener.isValid()) {
                    listener.onDispatchEnd(log);
                }
            }
        }
        synchronized (listeners) {
            for (DispatchListenerWrapper listener : listeners.values()) {
                if (listener.isValid()) {
                    listener.onDispatchEnd(log);
                }
            }
        }
    }
}

到了这里整个 LooperMonitor 就分析完成了