日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

BlockCanary原理解析

發布時間:2024/1/11 windows 54 coder
生活随笔 收集整理的這篇文章主要介紹了 BlockCanary原理解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、背景

為了解決應卡頓,分析耗時。

二、原理

Looper中的loop方法:

public static void loop() {
    ...

    for (;;) {
        ...

        // This must be in a local variable, in case a UI event sets the logger
        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);
        }

        ...
    }
}

可以看到在執行消息的時候,如果有設置logging,那么它會在消息開始與結束的時候打印出相關信息。如果主線程卡住了,就是在dispatchMessage這里卡住,所以我們可以通過計算這兩條log的時間差來判斷消息的執行時間。

我們可以通過這個方法來設置Printer。

Looper.getMainLooper().setMessageLogging(mainLooperPrinter);

三、源碼解析

application中調用初始化:
BlockCanary.install(this, AppBlockCanaryContext()).start()

最終會執行到:

    private BlockCanary() {
        BlockCanaryInternals.setContext(BlockCanaryContext.get());
        mBlockCanaryCore = BlockCanaryInternals.getInstance();
        mBlockCanaryCore.addBlockInterceptor(BlockCanaryContext.get());
        if (!BlockCanaryContext.get().displayNotification()) {
            return;
        }
        mBlockCanaryCore.addBlockInterceptor(new DisplayService());

    }

核心就是mBlockCanaryCore = BlockCanaryInternals.getInstance();它會對BlockCanaryInternals進行初始化。

    public BlockCanaryInternals() {

        stackSampler = new StackSampler(
                Looper.getMainLooper().getThread(),
                sContext.provideDumpInterval());

        cpuSampler = new CpuSampler(sContext.provideDumpInterval());

        setMonitor(new LooperMonitor(new LooperMonitor.BlockListener() {

            @Override
            public void onBlockEvent(long realTimeStart, long realTimeEnd,
                                     long threadTimeStart, long threadTimeEnd) {
                // Get recent thread-stack entries and cpu usage
                ArrayList<String> threadStackEntries = stackSampler
                        .getThreadStackEntries(realTimeStart, realTimeEnd);
                if (!threadStackEntries.isEmpty()) {
                    BlockInfo blockInfo = BlockInfo.newInstance()
                            .setMainThreadTimeCost(realTimeStart, realTimeEnd, threadTimeStart, threadTimeEnd)
                            .setCpuBusyFlag(cpuSampler.isCpuBusy(realTimeStart, realTimeEnd))
                            .setRecentCpuRate(cpuSampler.getCpuRateInfo())
                            .setThreadStackEntries(threadStackEntries)
                            .flushString();
                    LogWriter.save(blockInfo.toString());

                    if (mInterceptorChain.size() != 0) {
                        for (BlockInterceptor interceptor : mInterceptorChain) {
                            interceptor.onBlock(getContext().provideContext(), blockInfo);
                        }
                    }
                }
            }
        }, getContext().provideBlockThreshold(), getContext().stopWhenDebugging()));

        LogWriter.cleanObsolete();
    }

  • stackSampler:記錄棧相關信息
  • cpuSampler:記錄CPU相關信息
  • LooperMonitor:繼承Printer
    private void setMonitor(LooperMonitor looperPrinter) {
        monitor = looperPrinter;
    }

當我們調用BlockCanary的start方法的時候,便將其設給了Looper的printer,然后我們便可以在LooperMonitor的print方法里面去記錄打印的log的時間。

    public void start() {
        if (!mMonitorStarted) {
            mMonitorStarted = true;
            Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);
        }
    }

核心代碼:

    @Override
    public void println(String x) {
        if (mStopWhenDebugging && Debug.isDebuggerConnected()) {
            return;
        }
        if (!mPrintingStarted) {
            mStartTimestamp = System.currentTimeMillis();
            mStartThreadTimestamp = SystemClock.currentThreadTimeMillis();
            mPrintingStarted = true;
            startDump();
        } else {
            final long endTime = System.currentTimeMillis();
            mPrintingStarted = false;
            if (isBlock(endTime)) {
                notifyBlockEvent(endTime);
            }
            stopDump();
        }
    }

在開始執行消息的時候去記錄相關信息,結束消息的時候停止記錄相關信息,并且判斷消息執行的時間是否超過了我們設置的閾值,超過了的話便執行notifyBlockEvent(endTime);取出記錄的相關消息提示用戶。

說到此處,想到是不是可以用mainLooperPrinter來做更多事情呢?既然主線程都在這里,那只要parse出app包名的第一行,每次打印出來,是不是就不需要打點也能記錄出用戶操作路徑? 再者,比如想做onClick到頁面創建后的耗時統計,是不是也能用這個原理呢? 之后可以試試看這個思路(目前存在問題是獲取線程堆棧是定時3秒取一次的,很可能一些比較快的方法操作一下子完成了沒法在stacktrace里面反映出來)。

我們看一下怎么記錄棧以及cpu的消息的。

    private void startDump() {
        if (null != BlockCanaryInternals.getInstance().stackSampler) {
            BlockCanaryInternals.getInstance().stackSampler.start();
        }

        if (null != BlockCanaryInternals.getInstance().cpuSampler) {
            BlockCanaryInternals.getInstance().cpuSampler.start();
        }
    }

StackSampler與CpuSampler都繼承與AbstractSampler:
AbstractSampler里面的start方法:

    public void start() {
        if (mShouldSample.get()) {
            return;
        }
        mShouldSample.set(true);

        HandlerThreadFactory.getTimerThreadHandler().removeCallbacks(mRunnable);
        HandlerThreadFactory.getTimerThreadHandler().postDelayed(mRunnable,
                BlockCanaryInternals.getInstance().getSampleDelay());
    }

    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            doSample();

            if (mShouldSample.get()) {
                HandlerThreadFactory.getTimerThreadHandler()
                        .postDelayed(mRunnable, mSampleInterval);
            }
        }
    };


    long getSampleDelay() {
        return (long) (BlockCanaryInternals.getContext().provideBlockThreshold() * 0.8f);
    }

它其實是開了一個子線程每隔一定的時間就去記錄。

四、流程圖

五、總結

BlockCanary作為一個Android組件,目前還有局限性,因為其在一個完整的監控系統中只是一個生產者,還需要對應的消費者去分析日志,比如歸類排序,以便看出哪些卡慢更有修復價值,需要優先處理;又比如需要過濾機型,有些奇葩機型的問題造成的卡慢,到底要不要去修復是要斟酌的。扯遠一點的話,像是埋點除了統計外,完全還能用來做鏈路監控,比如一個完整的流程是A -> B -> D -> E, 但是某個時間節點突然A -> B -> D后沒有到達E,這時候監控平臺就可以發出預警,讓開發人員及時定位。很多監控方案都需要C/S兩端的配合。

總結

以上是生活随笔為你收集整理的BlockCanary原理解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。