騰訊 Apm 框架 Matrix 源碼閱讀 - TracePlugin 架構(gòu)解析

版本

v0.6.5

溫馨提示

TracePlugin 是比較復(fù)雜的,很多東西文章中可能講的不是很清楚,配合 推薦 Matrix 源碼完整注釋
可能會(huì)有更好的效果

概述

本篇文章是 騰訊開源的 APM 框架 Matrix 系列文章的第三篇,將對(duì) matrix-trace-canary這個(gè)模塊架構(gòu)進(jìn)行解析。這個(gè)模塊中包含了幀率(FPS)檢測(cè),啟動(dòng)時(shí)間檢測(cè)(APP啟動(dòng)和Activity啟動(dòng)),ANR檢測(cè),慢函數(shù)檢測(cè),這四個(gè)Tracer,后面我們會(huì)一一進(jìn)行分析。上一篇為騰訊 Apm 框架 Matrix 源碼閱讀 - 架構(gòu)解析

先看一下類圖及類功能的簡(jiǎn)介。

TraceCanary架構(gòu)圖 .jpg
  • LooperMonitor:通過給主線程Looper設(shè)置Printer來監(jiān)控 Looper分發(fā)事件的開始和結(jié)束。
  • UIThreadMonitor:反射 Choreographer并獲得每一個(gè)狀態(tài)的 CallbackQueue對(duì)象 和 mFrameIntervalNanos屬性值,配合 LooperMonitor使 LooperObserver具有感知每幀 開始,執(zhí)行,結(jié)束具體時(shí)間的能力。
  • LooperObserver:具有感知每個(gè)Message開始,執(zhí)行,結(jié)束具體時(shí)間的能力。
  • AppMethodBeat:還記得在 騰訊 Apm 框架 Matrix 源碼閱讀 - gradle插件中說了,在編譯器Matrix會(huì)對(duì)復(fù)雜方法進(jìn)行字節(jié)碼插樁,那插樁的是什么內(nèi)容呢?就是在復(fù)雜方法的入口插入 AppMethodBeat.i(),方法的出口插入AppMethodBeat.O()方法。在Activity的中插入 AppMethodBeat.at()方法。這個(gè)類主要作用就是收集各個(gè)方法的methodId和執(zhí)行時(shí)間并存放在自己的 sBuffer變量中,同時(shí)AppMethodBeat也能感知到某個(gè)activity是否獲取到焦點(diǎn),具體功能主要是在at方法中。
  • IAppForeground:在 騰訊 Apm 框架 Matrix 源碼閱讀 - 架構(gòu)解析 中提到過??梢愿兄?APP 進(jìn)入前臺(tái)還是 退出到后臺(tái)
  • ITracer: 繼承了 IAppForeground 是所有Tracer的父接口,具有 isAlive(),onStartTrace(),onCloseTrace()這三個(gè)抽象方法。
  • Tracer:是所有Tracer的直接父類,實(shí)現(xiàn)了ITracer接口繼承了LooperObserver類,通過上面說的Tracer天然具有 感知APP 進(jìn)入前臺(tái)還是 退出到后臺(tái)的能力和感知每個(gè)Message 開始,執(zhí)行,結(jié)束具體時(shí)間的能力。

下面我們就從TracePlugin這個(gè)入口類開始看。

1. TracePlugin

先看一下TracePlugin的構(gòu)造方法,沒什么可以說的,就是將配置傳進(jìn)來并進(jìn)行保存,配置中記錄了那些Tracer可用那些不可用,還記錄了我們?cè)O(shè)置的SplashActivity

    public TracePlugin(TraceConfig config) {
        this.traceConfig = config;
    }

  public class TraceConfig implements IDefaultConfig {
   ....
    public IDynamicConfig dynamicConfig;
    public boolean defaultFpsEnable;
    public boolean defaultMethodTraceEnable;
    public boolean defaultStartupEnable;
    public boolean defaultAnrEnable;
    public boolean isDebug;
    public boolean isDevEnv;
    public String splashActivities;//可配置多個(gè)用 ; 隔開,但是生效的還是只有第一個(gè)
    public Set<String> splashActivitiesSet;
   ....
}

1.1 TracePlugin.init()

騰訊 Apm 框架 Matrix 源碼閱讀 - 架構(gòu)解析中講過每個(gè)Plugin都具有一些生命周期方法,那么下來就依次看看TracePlugin在這些生命周期中做了那些事情。首先是 init()方法,可以看到init()方法中首先判斷當(dāng)前SDK的版本是否小于16如果小于16就直接返回了,如果大于等于16就創(chuàng)建各個(gè)Tracer。這里順便說一下 之所以把插件的最低支持版本設(shè)置為16 是因?yàn)?code>Choreographer.getInstance()方法是Api16才提供的

    @Override
    public void init(Application app, PluginListener listener) {
        //APi小于16 不支持
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            MatrixLog.e(TAG, "[FrameBeat] API is low Build.VERSION_CODES.JELLY_BEAN(16), TracePlugin is not supported");
            unSupportPlugin();
            return;
        }

        anrTracer = new AnrTracer(traceConfig);

        frameTracer = new FrameTracer(traceConfig);

        evilMethodTracer = new EvilMethodTracer(traceConfig);

        startupTracer = new StartupTracer(traceConfig);
    }

1.2 TracePlugin.start()

 @Override
    public void start() {
        super.start();
       ....
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                //初始化 UIThreadMonitor
                if (!UIThreadMonitor.getMonitor().isInit()) {
                    try {
                        //詳見【2.1】
                        UIThreadMonitor.getMonitor().init(traceConfig);
                    } catch (java.lang.RuntimeException e) {
                        return;
                    }
                }
                //啟動(dòng) AppMethodBeat 詳見【3.1】
                AppMethodBeat.getInstance().onStart();
                //啟動(dòng) UIThreadMonitor 詳見【2.9】
                UIThreadMonitor.getMonitor().onStart();
                anrTracer.onStartTrace();
                frameTracer.onStartTrace();
                evilMethodTracer.onStartTrace();
                startupTracer.onStartTrace();
            }
        };

        if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
            runnable.run();
        } else {
            //post到主線程啟動(dòng)
            MatrixHandlerThread.getDefaultMainHandler().post(runnable);
        }

    }

start()方法中主要是在主線程中啟動(dòng) UIThreadMonitor,AppMethodBeat還有各個(gè)Tracer

1.3 TracePlugin.stop()

    @Override
    public void stop() {
        .....
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                //詳見【3.2】
                AppMethodBeat.getInstance().onStop();
                UIThreadMonitor.getMonitor().onStop();
                anrTracer.onCloseTrace();
                frameTracer.onCloseTrace();
                evilMethodTracer.onCloseTrace();
                startupTracer.onCloseTrace();
            }
        };

        if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
            runnable.run();
        } else {
            MatrixHandlerThread.getDefaultMainHandler().post(runnable);
        }
    }

stop()方法中主要是在主線程中停止UIThreadMonitor,AppMethodBeat還有各個(gè)Tracer

1.4 TracePlugin.onForeground()

    @Override
    public void onForeground(boolean isForeground) {
       ....
        if (frameTracer != null) {
            frameTracer.onForeground(isForeground);
        }
        if (anrTracer != null) {
            anrTracer.onForeground(isForeground);
        }
        if (evilMethodTracer != null) {
            evilMethodTracer.onForeground(isForeground);
        }
        if (startupTracer != null) {
            startupTracer.onForeground(isForeground);
        }
    }

onForeground方法就是將APP處于前臺(tái)或者后臺(tái)的狀態(tài)分發(fā)給各個(gè)Tracer。可見TracePlugin中的工作還是蠻簡(jiǎn)單的就是初始化,啟動(dòng),分發(fā),暫停。

2.1 UIThreadMonitor.init(traceConfig)

  public void init(TraceConfig config) {
        //不是主線程 就拋出異常
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            throw new AssertionError("must be init in main thread!");
        }
        this.config = config;
        //從當(dāng)前線程中獲取到 Choreographer 對(duì)象
        choreographer = Choreographer.getInstance();
        // 獲得 Choreographer 里的 mLock鎖 對(duì)象
        callbackQueueLock = reflectObject(choreographer, "mLock");
        // 獲得 Choreographer 里的 mCallbackQueues 對(duì)象
        callbackQueues = reflectObject(choreographer, "mCallbackQueues");

        //反射獲得 callbackQueues 中第一個(gè) CallbackQueue對(duì)象的 addCallbackLocked 的方法
        // 第一個(gè) CallbackQueue 是處理 input事件的
        addInputQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_INPUT], ADD_CALLBACK, long.class, Object.class, Object.class);
        //反射獲得 callbackQueues 中第二個(gè) CallbackQueue對(duì)象的 addCallbackLocked 的方法
        // 第二個(gè) CallbackQueue 是處理 動(dòng)畫的
        addAnimationQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_ANIMATION], ADD_CALLBACK, long.class, Object.class, Object.class);
        //反射獲得 callbackQueues 中第三個(gè) CallbackQueue對(duì)象的 addCallbackLocked 的方法
        // 第三個(gè) CallbackQueue 是繪制完 用于回調(diào)的
        addTraversalQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_TRAVERSAL], ADD_CALLBACK, long.class, Object.class, Object.class);

        // 獲取 choreographer 中mFrameIntervalNanos 的值 并賦值給 frameIntervalNanos
        frameIntervalNanos = reflectObject(choreographer, "mFrameIntervalNanos");

        //注冊(cè)一個(gè) LooperDispatchListener 詳見【2.2】
        LooperMonitor.register(new LooperMonitor.LooperDispatchListener() {
            @Override
            public boolean isValid() {
                return isAlive;
            }

            @Override
            public void dispatchStart() {
                super.dispatchStart();
                //詳見【2.7】
                UIThreadMonitor.this.dispatchBegin();
            }

            @Override
            public void dispatchEnd() {
                super.dispatchEnd();
                //詳見【2.8】
                UIThreadMonitor.this.dispatchEnd();
            }

        });
        this.isInit = true;
        .....
    }

UIThreadMonitor.init主要做了三件事

  1. 反射獲得 CALLBACK_INPUT,CALLBACK_ANIMATION,CALLBACK_TRAVERSAL這三個(gè)狀態(tài)的addCallbackLocked并記錄到自己的成員變量中
  2. 反射獲得Choreographer記錄的mFrameIntervalNanos變量并記錄到自己的成員變量中。mFrameIntervalNanos的值在現(xiàn)在大部分手機(jī)設(shè)備上都是16666666
  3. LooperMonitor中注冊(cè)一個(gè)LooperDispatchListener監(jiān)聽用來監(jiān)控 Looper分發(fā)事件的開始和結(jié)束。

2.2 LooperMonitor.register()

     //餓漢式單例 詳見【2.3】
    private static final LooperMonitor mainMonitor = new LooperMonitor();

    static void register(LooperDispatchListener listener) {
        mainMonitor.addListener(listener);
    }

LooperMonitor.register沒啥可講,不過 LooperMonitor對(duì)象的創(chuàng)建使用了餓漢式單例的方式,我們一起看看它的構(gòu)造方法中干了什么

2.3 LooperMonitor <init>

    public LooperMonitor(Looper looper) {
        Objects.requireNonNull(looper);
        this.looper = looper;
        //添加 自定義的 Printer 詳見【2.4】
        resetPrinter();
        //添加 IdleHandler
        addIdleHandler(looper);
    }

    private LooperMonitor() {
        this(Looper.getMainLooper());
    }

可見無參構(gòu)造獲取到主線程Looper后調(diào)用到了有參構(gòu)造

2.4 LooperMonitor.resetPrinter()

  private synchronized void resetPrinter() {
        Printer originPrinter = null;
        try {
            if (!isReflectLoggingError) {
                //獲取之前的 Printer ,防止項(xiàng)目其他功能在 設(shè)置了 這個(gè) Printer, 如果直接設(shè)置
                //那其他 工具就不能正常運(yùn)行了 大廠的程序員 還是細(xì)?。?!
                originPrinter = ReflectUtils.get(looper.getClass(), "mLogging", looper);
                //如果已經(jīng) hook過 就直接返回
                if (originPrinter == printer && null != printer) {
                    return;
                }
            }
        } 
        ....
        //設(shè)置自己的Printer 詳見【2.5】
        looper.setMessageLogging(printer = new LooperPrinter(originPrinter));
        ....
    }

該方法主要通過反射獲取主線程LooperPrinter進(jìn)行保存,然后通過Api設(shè)置自定義的LooperPrinter。這里保存Looper之前的Printer主要是防止和其他框架沖突導(dǎo)致其他框架不能正常工作,再感嘆一次,大廠程序員還是細(xì)啊....

2.5 LooperPrinter.println()

  @Override
        public void println(String x) {
            if (null != origin) {
                //執(zhí)行原始printer的 println方法
                origin.println(x);
               ...
            }
           ....
            if (isValid) {
                //詳見【2.6】
                dispatch(x.charAt(0) == '>', x);
            }

        }

2.6 LooperPrinter.dispatch()

 private void dispatch(boolean isBegin, String log) {

        for (LooperDispatchListener listener : listeners) {
            if (listener.isValid()) {
                if (isBegin) {
                    if (!listener.isHasDispatchStart) {
                        //分發(fā)開始
                        listener.onDispatchStart(log);
                    }
                } else {
                    if (listener.isHasDispatchStart) {
                        //分發(fā)結(jié)束
                        listener.onDispatchEnd(log);
                    }
                }
            } else if (!isBegin && listener.isHasDispatchStart) {
                //分發(fā)結(jié)束
                listener.dispatchEnd();
            }
        }

    }

通過該方法就會(huì)通知給所有的LooperDispatchListener當(dāng)前是Looper剛開始分發(fā)還是已經(jīng)分發(fā)完成?!?.1】中說過UIThreadMonitor.init想注冊(cè)了一個(gè)LooperDispatchListener所以我們繼續(xù)回到 UIThreadMonitor類中

2.7 UIThreadMonitor.this.dispatchBegin

    private void dispatchBegin() {
        //記錄 dispatch 的起始時(shí)間
        token = dispatchTimeMs[0] = SystemClock.uptimeMillis();
        // 記錄 當(dāng)前線程時(shí)間
        dispatchTimeMs[2] = SystemClock.currentThreadTimeMillis();
        // 調(diào)用 i 方法
        AppMethodBeat.i(AppMethodBeat.METHOD_ID_DISPATCH);

        synchronized (observers) {
            //回調(diào) 所有 LooperObserver 的 dispatchBegin 方法
            for (LooperObserver observer : observers) {
                if (!observer.isDispatchBegin()) {
                    observer.dispatchBegin(dispatchTimeMs[0], dispatchTimeMs[2], token);
                }
            }
        }
    }

該方法中記錄了dispatch開始的時(shí)間并將 Looper開始分發(fā)事件 這個(gè)消息通知給了各個(gè) LooperObserver,我們?cè)谧铋_始介紹過Tracer類就繼承了LooperObserver這也就是所有Tracer都具有感知每個(gè)Message開始,執(zhí)行,結(jié)束具體時(shí)間的能力。

2.8 UIThreadMonitor.this.dispatchEnd

private void dispatchEnd() {

        //幀刷新結(jié)束
        if (isBelongFrame) {
             //詳見【2.14】
            doFrameEnd(token);
        }

        //dispatch 起始時(shí)間
        long start = token;
        //dispatch 結(jié)束時(shí)間
        long end = SystemClock.uptimeMillis();

        synchronized (observers) {
            //回調(diào) 所有 LooperObserver 的 doFrame 方法
            for (LooperObserver observer : observers) {
                if (observer.isDispatchBegin()) {
                    //參數(shù)含義 在 LooperObserver接口中查詢
                    observer.doFrame(AppMethodBeat.getVisibleScene(), token, SystemClock.uptimeMillis(), isBelongFrame ? end - start : 0, queueCost[CALLBACK_INPUT], queueCost[CALLBACK_ANIMATION], queueCost[CALLBACK_TRAVERSAL]);
                }
            }
        }

        //記錄 當(dāng)前線程時(shí)間
        dispatchTimeMs[3] = SystemClock.currentThreadTimeMillis();
        // 記錄 dispatch 的結(jié)束時(shí)間
        dispatchTimeMs[1] = SystemClock.uptimeMillis();
        // 調(diào)用 o 方法
        AppMethodBeat.o(AppMethodBeat.METHOD_ID_DISPATCH);

        synchronized (observers) {
            // 回調(diào) 所有 LooperObserver的 dispatchEnd 方法
            for (LooperObserver observer : observers) {
                if (observer.isDispatchBegin()) {
                    observer.dispatchEnd(dispatchTimeMs[0], dispatchTimeMs[2], dispatchTimeMs[1], dispatchTimeMs[3], token, isBelongFrame);
                }
            }
        }

    }

這個(gè)方法中 記了了各個(gè)時(shí)間點(diǎn)并回調(diào)了LooperObserverdoFramedispatchEnd方法。LooperObserver 中各個(gè)方法的各個(gè)參數(shù)的含義見附錄1

2.9 UIThreadMonitor.onStart()

    //標(biāo)識(shí)對(duì)應(yīng) type 執(zhí)行 開始 或者 結(jié)束
    private int[] queueStatus = new int[CALLBACK_LAST + 1];
    //type對(duì)應(yīng)的 執(zhí)行時(shí)間
    private long[] queueCost = new long[CALLBACK_LAST + 1];

    public synchronized void onStart() {
        if (!isInit) {
            throw new RuntimeException("never init!");
        }
        if (!isAlive) {
            this.isAlive = true;
            synchronized (this) {
                MatrixLog.i(TAG, "[onStart] callbackExist:%s %s", Arrays.toString(callbackExist), Utils.getStack());
                callbackExist = new boolean[CALLBACK_LAST + 1];
            }
            queueStatus = new int[CALLBACK_LAST + 1];
            queueCost = new long[CALLBACK_LAST + 1];
            //詳見【2.10】
            addFrameCallback(CALLBACK_INPUT, this, true);
        }
    }

這個(gè)方法主要是 重置queueStatusqueueCost這兩個(gè)重要的成員變量,然后調(diào)用addFrameCallback方法

2.10 UIThreadMonitor.addFrameCallback()

 private synchronized void addFrameCallback(int type, Runnable callback, boolean isAddHeader) {
        ....
        try {
            synchronized (callbackQueueLock) {//和 Choreographer 中使用相同的 鎖對(duì)象 都是 mLock
                Method method = null;
                switch (type) {
                    case CALLBACK_INPUT:
                        method = addInputQueue;
                        break;
                    case CALLBACK_ANIMATION:
                        method = addAnimationQueue;
                        break;
                    case CALLBACK_TRAVERSAL:
                        method = addTraversalQueue;
                        break;
                }
                if (null != method) {
                    //反射執(zhí)行 CallbackQueue 的 addCallbackLocked 方法 ,并將我們自己的 callback 添加到回到隊(duì)列中,在一幀繪制完畢后
                    // 會(huì)回調(diào)callback的run()方法
                    method.invoke(callbackQueues[type], !isAddHeader ? SystemClock.uptimeMillis() : -1, callback, null);
                    callbackExist[type] = true;
                }
            }
        } catch (Exception e) {
            MatrixLog.e(TAG, e.toString());
        }
    }

如果你對(duì)Choreographer這個(gè)類不太了解那你需要先看看Choreographer原理 。我們這里只對(duì)里面的vsync分類簡(jiǎn)單介紹:
Choreographervsync回調(diào)事件分四類

  • INPUT:輸入事件(輸入回調(diào),為了盡快響應(yīng)用戶操作)
  • ANIMATION:動(dòng)畫
  • TRAVERSAL:窗口刷新,執(zhí)行measure/layout/draw操作(處理布局和繪圖的回調(diào))
  • COMMIT:遍歷完成的提交操作,用來修正動(dòng)畫啟動(dòng)時(shí)間(繪制完成后的一些操作)

好了,我們繼續(xù)來看UIThreadMonitor.addFrameCallback,這個(gè)方法的功能就是將我們自定義的回調(diào)方法加入到Choreographer的回調(diào)隊(duì)列中(回調(diào)隊(duì)列有多種),當(dāng)一幀刷新完成后會(huì)調(diào)用我們回調(diào)的run方法,因?yàn)椤?.9】中的回調(diào)對(duì)象是this所以我們接下來進(jìn)入到UIThreadMonitor.run

2.11 UIThreadMonitor.run

  @Override
    public void run() {
        final long start = System.nanoTime();
        try {
            //詳見 【2.12】
            doFrameBegin(token);
            //詳見 【2.13】
            doQueueBegin(CALLBACK_INPUT);//input開始
            addFrameCallback(CALLBACK_ANIMATION, new Runnable() {
                @Override
                public void run() {
                    //詳見 【2.14】
                    doQueueEnd(CALLBACK_INPUT);//input 結(jié)束
                    doQueueBegin(CALLBACK_ANIMATION);//animation 開始
                }
            }, true);
            addFrameCallback(CALLBACK_TRAVERSAL, new Runnable() {
                @Override
                public void run() {
                    doQueueEnd(CALLBACK_ANIMATION);//animation 結(jié)束
                    doQueueBegin(CALLBACK_TRAVERSAL);//traversal 開始
                }
            }, true);

        } finally {
            ....
            }
        }
    }

這個(gè)方法的功能就是添加CALLBACK_ANIMATIONCALLBACK_TRAVERSAL類型的回到到Choreographer的回調(diào)隊(duì)列中因?yàn)?code>CALLBACK_INPUT類型的回調(diào)已經(jīng)在UIThreadMonitor.onStart中已經(jīng)添加了所以這里不需要添加。

之所以為每種Type都添加監(jiān)聽?wèi)?yīng)該是 Matrix 設(shè)計(jì)的時(shí)候計(jì)劃將每一幀的耗時(shí)細(xì)化到每種Type。否則不用這么麻煩直接調(diào)用提供的apichoreographer.postFrameCallback就可以拿到每幀耗時(shí)

2.12 UIThreadMonitor.doFrameBegin

    private void doFrameBegin(long token) {
        //記錄幀刷新開始
        this.isBelongFrame = true;
    }

2.13 UIThreadMonitor.doQueueBegin

    private void doQueueBegin(int type) {
        //標(biāo)識(shí)當(dāng)前 type 開始執(zhí)行
        queueStatus[type] = DO_QUEUE_BEGIN;
        // 當(dāng)前type 回調(diào)開始時(shí)間
        queueCost[type] = System.nanoTime();
    }

2.13 UIThreadMonitor.doQueueEnd

    private void doQueueEnd(int type) {
        //標(biāo)識(shí)當(dāng)前 type 執(zhí)行結(jié)束
        queueStatus[type] = DO_QUEUE_END;
        // 當(dāng)前type 執(zhí)行耗時(shí)
        queueCost[type] = System.nanoTime() - queueCost[type];
        synchronized (this) {
            // 當(dāng)前type的 callback 是否還存在
            callbackExist[type] = false;
        }
    }

2.14 UIThreadMonitor.doFrameEnd

  // 記錄 幀結(jié)束
    private void doFrameEnd(long token) {

        doQueueEnd(CALLBACK_TRAVERSAL);//traversal 結(jié)束

        // 如果有 沒有結(jié)束的回調(diào) 則報(bào)錯(cuò)
        for (int i : queueStatus) {
            if (i != DO_QUEUE_END) {
                queueCost[i] = DO_QUEUE_END_ERROR;
                if (config.isDevEnv) {
                    throw new RuntimeException(String.format("UIThreadMonitor happens type[%s] != DO_QUEUE_END", i));
                }
            }
        }

        //重置 queueStatus
        queueStatus = new int[CALLBACK_LAST + 1];

        //繼續(xù)添加  input callback
        addFrameCallback(CALLBACK_INPUT, this, true);

        this.isBelongFrame = false;
    }

在【2.8】UIThreadMonitor.this.dispatchEnd方法中 ,也就是當(dāng)一個(gè)Message分發(fā)完畢的時(shí)候調(diào)用了doFrameEnd

該方法主要是重置 queueStatus,并給Choreographer的回調(diào)隊(duì)列中重新添加CALLBACK_INPUT類型的回到方法。作為下一次記錄幀刷新時(shí)間的開始。

看到這里可能有人有疑惑,不是之前添加過了么,為啥這里還要再添加一次。是因?yàn)?這種監(jiān)聽是一次性的,Choreographer.doCallbacks()方法最后會(huì)將所有的監(jiān)聽都會(huì)移除.

2.15 總結(jié)一下 UIThreadMonitor的工作原理

  1. 主線程Looper loop 到一個(gè) target是 Choreographer$FrameHandler massage 是 Choreographer$FrameDisplayEventReceiver 的消息(要能觸發(fā) 刷新幀)
  2. 調(diào)用 dispatchBegin() 方法
  3. 調(diào)用 run方法
  4. 回掉 doQueueBegin type=0(input)
  5. 回掉 doQueueEnd type=0(input)
  6. 回掉 doQueueBegin type=1(animation)
  7. 回掉 doQueueEnd type=1(animation)
  8. 回掉 doQueueBegin type=2(traversal)
  9. Looper中message處理完畢
  10. 調(diào)用 dispatchEnd() 方法
  11. 回掉 doQueueEnd type=2(traversal)

這就是 UIThreadMonitor獲取一幀中每種Type耗時(shí)的流程。這個(gè)過程中UIThreadMonitor會(huì)將自己的獲取到的時(shí)間通過LooperObserver這個(gè)接口分發(fā)出去。

注意:當(dāng)主線程的 Looper loop 到的Message不能觸發(fā)幀刷新,那么就只會(huì)執(zhí)行 dispatchBegindispatchEnd這兩個(gè)方法。

3.1 AppMethodBeat.onStart()

下來我們繼續(xù)看AppMethodBeat是怎么工作的

 public void onStart() {
        synchronized (statusLock) {
            //如果沒有啟動(dòng) 或者已經(jīng)過期 則進(jìn)行啟動(dòng)
            if (status < STATUS_STARTED && status >= STATUS_EXPIRED_START) {
                //取消 啟動(dòng)過期 檢查的 Runnable
                sHandler.removeCallbacks(checkStartExpiredRunnable);
                if (sBuffer == null) {
                    throw new RuntimeException(TAG + " sBuffer == null");
                }
                MatrixLog.i(TAG, "[onStart] preStatus:%s", status, Utils.getStack());
                //標(biāo)示已將 啟動(dòng)
                status = STATUS_STARTED;
            } else {
                MatrixLog.w(TAG, "[onStart] current status:%s", status);
            }
        }
    }

onStart()主要是將AppMethodBeat 的當(dāng)前狀態(tài)設(shè)置為STATUS_STARTED。AppMethodBeat 中各個(gè)狀態(tài)含義及觸發(fā)時(shí)機(jī)可查看【附錄.2】

3.2 AppMethodBeat.onStop()

    public void onStop() {
        synchronized (statusLock) {
            //進(jìn)行關(guān)閉
            if (status == STATUS_STARTED) {
                MatrixLog.i(TAG, "[onStop] %s", Utils.getStack());
                status = STATUS_STOPPED;
            } else {
                MatrixLog.w(TAG, "[onStop] current status:%s", status);
            }
        }
    }

onStop()主要是將AppMethodBeat 的當(dāng)前狀態(tài)設(shè)置為STATUS_STOPPED。

3.3 AppMethodBeat.i()

在最開始描述AppMethodBeat類的時(shí)候,提到過在編譯器會(huì)將AppMethodBeati(),o(),at()方法插樁到指定位置那么我們就來看看,這三個(gè)方法具體過了那些工作

   public static void i(int methodId) {
        ....
        //第一次執(zhí)行該方法時(shí) 調(diào)用 realExecute 方法,并將狀態(tài)切換為 STATUS_READY
        if (status == STATUS_DEFAULT) {
            synchronized (statusLock) {
                if (status == STATUS_DEFAULT) {
                    //當(dāng) 當(dāng)前類 沒有被啟動(dòng)時(shí) 執(zhí)行該方法 詳見【3.4】
                    realExecute();
                    //切換狀態(tài) 為 STATUS_READY
                    status = STATUS_READY;
                }
            }
        }

        long threadId = Thread.currentThread().getId();
        //執(zhí)行回調(diào)
        if (sMethodEnterListener != null) {
            sMethodEnterListener.enter(methodId, threadId);
        }

        //如果是主線程
        if (threadId == sMainThreadId) {

            //i方法被重復(fù)執(zhí)行的 提醒
            if (assertIn) {
                android.util.Log.e(TAG, "ERROR!!! AppMethodBeat.i Recursive calls!!!");
                return;
            }
            assertIn = true;
            if (sIndex < Constants.BUFFER_SIZE) {
                //詳見【3.9】
                mergeData(methodId, sIndex, true);
            } else {
                sIndex = 0;
                mergeData(methodId, sIndex, true);
            }
            ++sIndex;
            assertIn = false;
        }
    }

在第一次進(jìn)入i()的時(shí)候會(huì)觸發(fā)調(diào)用realExecute()方法進(jìn)行一些初始化工作,然后調(diào)用mergeData()方法保持調(diào)用時(shí)的methodId和時(shí)間到sBuffer

3.4 AppMethodBeat.realExecute()

 //這個(gè)方法只有 在第一次執(zhí)行 i方法時(shí)才會(huì)被調(diào)用
    private static void realExecute() {
        MatrixLog.i(TAG, "[realExecute] timestamp:%s", System.currentTimeMillis());

        //當(dāng)前時(shí)間減去上一個(gè) 記錄的時(shí)間 (更新 sCurrentDiffTime)
        sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;

        //清空 sHandler 所有消息
        sHandler.removeCallbacksAndMessages(null);
        //延遲5 ms 后執(zhí)行 sUpdateDiffTimeRunnable ,開始刷新  sCurrentDiffTime 詳見【3.5】
        sHandler.postDelayed(sUpdateDiffTimeRunnable, Constants.TIME_UPDATE_CYCLE_MS);
        //延遲15 ms 后執(zhí)行 checkStartExpiredRunnable (檢查 AppMethodBeat 當(dāng)前狀態(tài)的 runnable )
        //也就是 在 realExecute 方法之后后 如果 15ms 內(nèi) AppMethodBeat 還沒有被啟動(dòng)(onStart)
        // 就將 AppMethodBeat的狀態(tài)置為 STATUS_EXPIRED_START(啟動(dòng)過期)
        // 啟動(dòng)過期 只是一種狀態(tài),并不會(huì)影響 AppMethodBeat 的運(yùn)行
        sHandler.postDelayed(checkStartExpiredRunnable = new Runnable() {
            @Override
            public void run() {
                synchronized (statusLock) {
                    MatrixLog.i(TAG, "[startExpired] timestamp:%s status:%s", System.currentTimeMillis(), status);
                    if (status == STATUS_DEFAULT || status == STATUS_READY) {
                        status = STATUS_EXPIRED_START;
                    }
                }
            }
        }, Constants.DEFAULT_RELEASE_BUFFER_DELAY);

        //hook 主線程的 HandlerCallback 詳見【3.6】
        ActivityThreadHacker.hackSysHandlerCallback();
        //注冊(cè) looperMonitorListener 使可以接收到looper分發(fā)massage事件 詳見【3.8】
        LooperMonitor.register(looperMonitorListener);
    }

3.5 AppMethodBeat.sUpdateDiffTimeRunnable

 private static Runnable sUpdateDiffTimeRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                //無限循環(huán)  當(dāng)isPauseUpdateTime=false(dispatchBegin方法完成),然后更新 sCurrentDiffTime
                while (true) {
                    while (!isPauseUpdateTime && status > STATUS_STOPPED) {
                        sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
                       SystemClock.sleep(Constants.TIME_UPDATE_CYCLE_MS);
                    }
                    synchronized (updateTimeLock) {
                        updateTimeLock.wait();
                    }
                }
            } catch (InterruptedException e) {
                MatrixLog.e(TAG, "" + e.toString());
            }
        }
    };

對(duì)于這個(gè)Runnable Matrix的wiki中有介紹,其中這樣描述到:

考慮到每個(gè)方法執(zhí)行前后都獲取系統(tǒng)時(shí)間(System.nanoTime)會(huì)對(duì)性能影響比較大,而實(shí)際上,單個(gè)函數(shù)執(zhí)行耗時(shí)小于 5ms 的情況,對(duì)卡頓來說不是主要原因,可以忽略不計(jì),如果是多次調(diào)用的情況,則在它的父級(jí)方法中可以反映出來,所以為了減少對(duì)性能的影響,通過另一條更新時(shí)間的線程每 5ms 去更新一個(gè)時(shí)間變量,而每個(gè)方法執(zhí)行前后只讀取該變量來減少性能損耗。

3.6 ActivityThreadHacker.hackSysHandlerCallback()

public static void hackSysHandlerCallback() {
        try {
            //當(dāng)前方法加載的時(shí)間 被認(rèn)為是 APP啟動(dòng)時(shí)間,
            sApplicationCreateBeginTime = SystemClock.uptimeMillis();
            //記錄這第一個(gè)方法,
            sApplicationCreateBeginMethodIndex = AppMethodBeat.getInstance().maskIndex("ApplicationCreateBeginMethodIndex");

            //替換 ActivityThread 中handler的 callBack 方法
            Class<?> forName = Class.forName("android.app.ActivityThread");
            Field field = forName.getDeclaredField("sCurrentActivityThread");
            field.setAccessible(true);
            //獲得 ActivityThread 對(duì)象
            Object activityThreadValue = field.get(forName);
            Field mH = forName.getDeclaredField("mH");
            mH.setAccessible(true);
            //獲得handler對(duì)象
            Object handler = mH.get(activityThreadValue);
            Class<?> handlerClass = handler.getClass().getSuperclass();
            Field callbackField = handlerClass.getDeclaredField("mCallback");
            callbackField.setAccessible(true);
            //獲得 Handler.Callback 對(duì)象
            Handler.Callback originalCallback = (Handler.Callback) callbackField.get(handler);
            //詳見【3.7】
            HackCallback callback = new HackCallback(originalCallback);
            //設(shè)置新的callback對(duì)象
            callbackField.set(handler, callback);
            MatrixLog.i(TAG, "hook system handler completed. start:%s SDK_INT:%s", sApplicationCreateBeginTime, Build.VERSION.SDK_INT);
        } catch (Exception e) {
            MatrixLog.e(TAG, "hook system handler err! %s", e.getCause().toString());
        }
    }

這個(gè)方法有兩個(gè)主要作用

  1. 記錄sApplicationCreateBeginTime這個(gè)時(shí)間,這個(gè)時(shí)間被認(rèn)為是APP啟動(dòng)時(shí)間。
    因?yàn)樵诓鍢兜倪^程中忽略了 android包下的所有類,所以運(yùn)行期起一個(gè)執(zhí)行 AppMethodBeat.i方法的應(yīng)該是 Application的attachBaseContext方法(如果有),下來就是onCreate()方法。所以這個(gè)時(shí)間被認(rèn)為是APP啟動(dòng)時(shí)間是沒有毛病的。
  2. hookActivityThreadmHHandler.Callback對(duì)象為自定義的HackCallback對(duì)象主要用來記錄最近一個(gè)Activity被打開的時(shí)間

3.7 ActivityThreadHacker.HackCallback.handleMessage()

 public boolean handleMessage(Message msg) {

            if (!AppMethodBeat.isRealTrace()) {
                //將 handleMessage 的控制權(quán)交還給 mOriginalCallback,再一次感嘆真細(xì)啊,哈哈
                return null != mOriginalCallback && mOriginalCallback.handleMessage(msg);
            }
            //是否是打開activity的handler信息
            boolean isLaunchActivity = isLaunchActivity(msg);
             .....
            //打開一次activity,就記錄一次數(shù)據(jù)
            if (isLaunchActivity) {
                ActivityThreadHacker.sLastLaunchActivityTime = SystemClock.uptimeMillis();
                ActivityThreadHacker.sLastLaunchActivityMethodIndex = AppMethodBeat.getInstance().maskIndex("LastLaunchActivityMethodIndex");
            }

            if (!isCreated) {
                if (isLaunchActivity || msg.what == CREATE_SERVICE || msg.what == RECEIVER) { // 如果是啟動(dòng)activity、service,receiver
                    //發(fā)送啟動(dòng)Activity等消息,認(rèn)為是Application 啟動(dòng)的結(jié)束時(shí)間
                    ActivityThreadHacker.sApplicationCreateEndTime = SystemClock.uptimeMillis();
                    ActivityThreadHacker.sApplicationCreateScene = msg.what;
                    isCreated = true;
                }
            }

            //將 handleMessage 的控制權(quán)交還給 mOriginalCallback
            return null != mOriginalCallback && mOriginalCallback.handleMessage(msg);
        }

該方法的主要租用也有兩個(gè)

  1. 記錄最近一個(gè)Activity的啟動(dòng)時(shí)間
  2. 第一次進(jìn)入時(shí) 記錄 Application 的 啟動(dòng)結(jié)束時(shí)間

3.8 AppMethodBeat.looperMonitorListener

    private static LooperMonitor.LooperDispatchListener looperMonitorListener = new LooperMonitor.LooperDispatchListener() {
        @Override
        public boolean isValid() {
            return status >= STATUS_READY;
        }

        @Override
        public void dispatchStart() {
            super.dispatchStart();
            AppMethodBeat.dispatchBegin();
        }

        @Override
        public void dispatchEnd() {
            super.dispatchEnd();
            AppMethodBeat.dispatchEnd();
        }
    };

 private static void dispatchBegin() {
        //更新 sCurrentDiffTime
        sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
        isPauseUpdateTime = false;
        SystemClock.uptimeMillis(),sCurrentDiffTime);
        synchronized (updateTimeLock) {
            updateTimeLock.notify();
        }
    }

    private static void dispatchEnd() {
        isPauseUpdateTime = true;
    }

上面我們已經(jīng)了解過 LooperMonitor可以感知到主線程Looper開始分發(fā)和結(jié)束分發(fā)Message的實(shí)際,AppMethodBeat注冊(cè)looperMonitorListener監(jiān)聽到 LooperMonitor主要是 為了在主線程空閑的情況下停止更新sCurrentDiffTime在主線程開始工作時(shí)再開始更新sCurrentDiffTime以減少資源的占用。

3.9 AppMethodBeat.mergeData()

   private static void mergeData(int methodId, int index, boolean isIn) {
        if (methodId == AppMethodBeat.METHOD_ID_DISPATCH) {//如果是 handler 的 dispatchMessage 方法
            sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
        }
        // 合并后的數(shù)據(jù)存到 trueId中
        long trueId = 0L;
        if (isIn) {//如果是 i 方法 則第63位上是1,否則為0 (就是一個(gè)標(biāo)志位)
            trueId |= 1L << 63;
        }
        //43-62位 存儲(chǔ) methodId
        trueId |= (long) methodId << 43;
        //0-42位存儲(chǔ) sCurrentDiffTime
        trueId |= sCurrentDiffTime & 0x7FFFFFFFFFFL;
        //存放到 sBuffer中
        sBuffer[index] = trueId;
        checkPileup(index);
        sLastIndex = index;
    }

這個(gè)方法的第三個(gè)參數(shù)isIn的含義為是否是 i()方法,i()方法中被調(diào)用為 trueo()方法中被調(diào)用為false。

mergeData其實(shí)就做了一件事,就是將方法類型(i或o),methodId,sCurrentDiffTime存到sBuffer中的index位置

3.10 AppMethodBeat.o()

    public static void o(int methodId) {
        //對(duì) AppMethodBeat 狀態(tài)進(jìn)行檢查
        if (status <= STATUS_STOPPED) {
            return;
        }
        //對(duì) methodId進(jìn)行校驗(yàn)
        if (methodId >= METHOD_ID_MAX) {
            return;
        }

        //如果是主線程
        if (Thread.currentThread().getId() == sMainThreadId) {
            if (sIndex < Constants.BUFFER_SIZE) {
                mergeData(methodId, sIndex, false);
            } else {
                sIndex = 0;
                mergeData(methodId, sIndex, false);
            }
            ++sIndex;
        }
    }

i()方法一樣最終也是調(diào)用了mergeData()來存儲(chǔ)數(shù)據(jù)到sBuffer

3.11 AppMethodBeat.at()

    public static void at(Activity activity, boolean isFocus) {
        String activityName = activity.getClass().getName();
        if (isFocus) {
            //獲取焦點(diǎn)的activity 添加到 sFocusActivitySet
            if (sFocusActivitySet.add(activityName)) {
                synchronized (listeners) {
                    //廣播 activityName 獲取到焦點(diǎn)
                    for (IAppMethodBeatListener listener : listeners) {
                        listener.onActivityFocused(activityName);
                    }
                }
                // activity 不都有了嗎 為啥還要通過 getVisibleScene() 獲???
                MatrixLog.i(TAG, "[at] visibleScene[%s] has %s focus!", getVisibleScene(), "attach");
            }
        } else {
            //失去焦點(diǎn)的activity 從sFocusActivitySet移除
            if (sFocusActivitySet.remove(activityName)) {
                MatrixLog.i(TAG, "[at] visibleScene[%s] has %s focus!", getVisibleScene(), "detach");
            }
        }
    }

在剛開始我們描述AppMethodBeat這個(gè)類的時(shí)候提到過,它能感知到某個(gè)activity是否獲取到焦點(diǎn)。這是因?yàn)?code>at()方法會(huì)在編譯器被插入到 Activity的onWindowFocusChanged()方法中。

at()方法主要的功能是自己獲得的焦點(diǎn)信息分發(fā)給每一個(gè)IAppMethodBeatListener

3.12 AppMethodBeat.realRelease()

    static {
        //在 AppMethodBeat 類加載 15s 后,還沒有使用(status的狀態(tài)還是STATUS_DEFAULT),就清空AppMethodBeat 占用的內(nèi)存
        sHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                realRelease();
            }
        }, Constants.DEFAULT_RELEASE_BUFFER_DELAY);
    }

    private static void realRelease() {
        synchronized (statusLock) {
            if (status == STATUS_DEFAULT) {
                MatrixLog.i(TAG, "[realRelease] timestamp:%s", System.currentTimeMillis());
                sHandler.removeCallbacksAndMessages(null);
                //移除 looperMonitorListener 監(jiān)聽
                LooperMonitor.unregister(looperMonitorListener);
                //sTimerUpdateThread 退出
                sTimerUpdateThread.quit();
                sBuffer = null;
                //狀態(tài)改為 STATUS_OUT_RELEASE
                status = STATUS_OUT_RELEASE;
            }
        }
    }

這個(gè)方法主要是在 AppMethodBeat 類加載 15s 后,還沒有使用的情況下釋放各種資源,尤其是sBuffer因?yàn)樗驼加昧?.6M的內(nèi)存。

總結(jié)

  1. UIThreadMonitor配合LooperMonitor獲得每個(gè)刷新幀的各個(gè)階段的耗時(shí)時(shí)間
  2. AppMethodBeat中記錄了Application的啟動(dòng)時(shí)間和結(jié)束,Activity的啟動(dòng)時(shí)間和結(jié)束,每個(gè)方法的耗時(shí)時(shí)間。

系列文章

參考資料

附錄

  1. LooperObserver中各個(gè)參數(shù)的含義
 /**
     *
     * @param beginMs dispatch 的起始時(shí)間
     * @param cpuBeginMs dispatch 的起始時(shí) 當(dāng)前線程時(shí)間
     * @param token 等于 dispatch 的起始時(shí)間
     */
    @CallSuper
    public void dispatchBegin(long beginMs, long cpuBeginMs, long token) {
        isDispatchBegin = true;
    }

    /**
     *
     * @param focusedActivityName 當(dāng)前activity的名字
     * @param start looper dispatch 的起始時(shí)間
     * @param end looper dispatch 的結(jié)束時(shí)間
     * @param frameCostMs 該幀耗時(shí)
     * @param inputCostNs input 花費(fèi) 時(shí)間
     * @param animationCostNs animation 花費(fèi) 時(shí)間
     * @param traversalCostNs traversal 花費(fèi) 時(shí)間
     */
    public void doFrame(String focusedActivityName, long start, long end, long frameCostMs, long inputCostNs, long animationCostNs, long traversalCostNs) {

    }

    /**
     *
     * @param beginMs dispatch 的起始時(shí)間
     * @param cpuBeginMs dispatch 的起始時(shí) 當(dāng)前線程時(shí)間
     * @param endMs dispatch 的結(jié)束時(shí)間
     * @param cpuEndMs dispatch 的結(jié)束時(shí) 當(dāng)前線程時(shí)間
     * @param token 等于 dispatch 的起始時(shí)間
     * @param isBelongFrame 是否屬于一幀? 不確定
     */
    @CallSuper
    public void dispatchEnd(long beginMs, long cpuBeginMs, long endMs, long cpuEndMs, long token, boolean isBelongFrame) {
        isDispatchBegin = false;
    }
  1. AppMethodBeat中各個(gè)狀態(tài)含義及觸發(fā)時(shí)機(jī)可查看
    //默認(rèn)狀態(tài)
    private static final int STATUS_DEFAULT = Integer.MAX_VALUE;
    //調(diào)用onStart()后的狀態(tài)
    private static final int STATUS_STARTED = 2; //啟動(dòng)
    //第一次 執(zhí)行 i 方法后的狀態(tài)
    private static final int STATUS_READY = 1; // 準(zhǔn)備好
    //調(diào)用onStop()后的狀態(tài)
    private static final int STATUS_STOPPED = -1; //停止
    //啟動(dòng)已過期 當(dāng) 在 realExecute 方法之后后 如果 15ms 內(nèi) AppMethodBeat 還沒有被啟動(dòng)(onStart)就會(huì)被置為這種狀態(tài)
    private static final int STATUS_EXPIRED_START = -2;
    //在 AppMethodBeat 類加載 15s 后,還沒有使用(status的狀態(tài)還是STATUS_DEFAULT),就會(huì)被置為這種狀態(tài)
    private static final int STATUS_OUT_RELEASE = -3;//已釋放
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容