BlockCanary原理分析

概述

BlockCanary是Android平臺上的一個輕量的,非侵入式的性能監(jiān)控組件,可以在使用應用的時候檢測主線程上的各種卡頓問題,并可通過組件提供的各種信息分析出原因并進行修復。

使用

項目地址:https://github.com/markzhai/AndroidPerformanceMonitor

Step1. 配置build.gradle

dependencies {
    // most often used way, enable notification to notify block event
    implementation 'com.github.markzhai:blockcanary-android:1.5.0'

    // this way you only enable BlockCanary in debug package
    // debugImplementation 'com.github.markzhai:blockcanary-android:1.5.0'
    // releaseImplementation 'com.github.markzhai:blockcanary-no-op:1.5.0'
}

Step2. 在Application中注冊

public class DemoApplication extends Application {
    @Override
    public void onCreate() {
        // ...
        // Do it on main process
        BlockCanary.install(this, new BlockCanaryContext()).start();
    }
}

Step3. 檢測結果

blockcanary-result.png

原理

在Android中,應用的卡頓,主要是在主線程阻塞導致的。Looper是主線程的消息調度者,所以以它為突破點。

Looper#loop()

在Looper的loop方法中,有一個Printer,它在每個Message處理的前后被調用,而如果主線程卡住了,就是dispatchMessage里卡住了。

public static void loop() {
    // ....

    for (;;) {
        // ...
        
        // 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);
        }

        // ...
        msg.target.dispatchMessage(msg);
        // ...

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
       // ...
    }
}

獲取主線程的Looper

因為Looper在每個線程最多只有一個實例,所以只要獲取到主線程的Looper,就可以設置一個自定義的Printer對象到里面。

Looper mainLooper = Looper.getMainLooper();

創(chuàng)建自定義Printer

在Printer的println方法去計算主線程一條Message處理的時長,當時長超過設定的閾值時就判定是卡頓了。

...
@Override
public void println(String x) {
    if (!mStartedPrinting) {
        mStartTimeMillis = System.currentTimeMillis();
        mStartThreadTimeMillis = SystemClock.currentThreadTimeMillis();
        mStartedPrinting = true;
    } else {
        final long endTime = System.currentTimeMillis();
        mStartedPrinting = false;
        if (isBlock(endTime)) {
            notifyBlockEvent(endTime);
        }
    }
}

private boolean isBlock(long endTime) {
    return endTime - mStartTimeMillis > mBlockThresholdMillis;
}
...

設置自定義Printer到主線程Looper

Looper.getMainLooper().setMessageLogging(mainLooperPrinter);

流程圖

blockcanary-flow.png

參考鏈接

  1. BlockCanary — 輕松找出Android App界面卡頓元兇
編碼前線.jpg
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容