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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android blockcanary 原理,blockCanary原理

發布時間:2024/9/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android blockcanary 原理,blockCanary原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

blockCanary

對于android里面的性能優化,最主要的問題就是UI線程的阻塞導致的,對于如何準確的計算UI的繪制所耗費的時間,是非常有必要的,blockCanary是基于這個需求出現的,同樣的,也是基于LeakCanary,和LeakCanary有著顯示頁面和堆棧信息。

使用

首先在gradle引入

implementation 'com.github.markzhai:blockcanary-android:1.5.0'

然后Application里面進行初始化和start

BlockCanary.install(this, new BlockCanaryContext()).start();

原理:

其中BlockCanaryContext表示的就是我們監測的某些參數,包括卡頓的閾值、輸出文件的路徑等等

//默認卡頓閾值為1000ms

public int provideBlockThreshold() {

return 1000;

}

//輸出的log

public String providePath() {

return "/blockcanary/";

}

//支持文件上傳

public void upload(File zippedFile) {

throw new UnsupportedOperationException();

}

//可以在卡頓提供自定義操作

@Override

public void onBlock(Context context, BlockInfo blockInfo) {

}

其中,init只是創建出BlockCanary實例。主要是start方法的操作。

/**

* Start monitoring.

*/

public void start() {

if (!mMonitorStarted) {

mMonitorStarted = true;

Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);

}

}

其實就是給主線程的Looper設置一個monitor。

我們可以先看看主線程的looper實現。

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);

}

...

}

}

在上面的loop循環的代碼中,msg.target.dispatchMessage就是我們UI線程收到每一個消息需要執行的操作,都在其內部執行。

系統也在其執行的前后都會執行logging類的print的方法,這個方法是我們可以自定義的。所以只要我們在運行的前后都添加一個時間戳,用運行后的時間減去運行前的時間,一旦這個時間超過了我們設定的閾值,那么就可以說這個操作卡頓,阻塞了UI線程,最后通過dump出此時的各種信息,來分析各種性能瓶頸。

那么接下來可以看看這個monitor的println方法。

@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();

}

}

在ui操作執行前,將會記錄當前的時間戳,同時會startDump。

在ui操作執行后,將會計算當前是否卡頓了,如果卡頓了,將會回調到onBlock的onBlock方法。同時將會停止dump。

為什么操作之前就開啟了startDump,而操作執行之后就stopDump呢?

private void startDump() {

if (null != BlockCanaryInternals.getInstance().stackSampler) {

BlockCanaryInternals.getInstance().stackSampler.start();

}

if (null != BlockCanaryInternals.getInstance().cpuSampler) {

BlockCanaryInternals.getInstance().cpuSampler.start();

}

}

public void start() {

if (mShouldSample.get()) {

return;

}

mShouldSample.set(true);

HandlerThreadFactory.getTimerThreadHandler().removeCallbacks(mRunnable);

HandlerThreadFactory.getTimerThreadHandler().postDelayed(mRunnable,

BlockCanaryInternals.getInstance().getSampleDelay());

}

其實startDump的時候并沒有馬上start,而是會postDelay一個runnable,這個runnable就是執行dump的真正的操作,delay的時間就是我們設置的閾值的0.8

也就是,一旦我們的stop在設置的延遲時間之前執行,就不會真正的執行dump操作。

public void stop() {

if (!mShouldSample.get()) {

return;

}

mShouldSample.set(false);

HandlerThreadFactory.getTimerThreadHandler().removeCallbacks(mRunnable);

}

只有當stop操作在設置的延遲時間之后執行,才會執行dump操作。

private Runnable mRunnable = new Runnable() {

@Override

public void run() {

doSample();

if (mShouldSample.get()) {

HandlerThreadFactory.getTimerThreadHandler()

.postDelayed(mRunnable, mSampleInterval);

}

}

};

這個doSameple分別會dump出stack信息和cpu信息。

cpu:

try {

cpuReader = new BufferedReader(new InputStreamReader(

new FileInputStream("/proc/stat")), BUFFER_SIZE);

String cpuRate = cpuReader.readLine();

if (cpuRate == null) {

cpuRate = "";

}

if (mPid == 0) {

mPid = android.os.Process.myPid();

}

pidReader = new BufferedReader(new InputStreamReader(

new FileInputStream("/proc/" + mPid + "/stat")), BUFFER_SIZE);

String pidCpuRate = pidReader.readLine();

if (pidCpuRate == null) {

pidCpuRate = "";

}

parse(cpuRate, pidCpuRate);

}

stack:

protected void doSample() {

StringBuilder stringBuilder = new StringBuilder();

for (StackTraceElement stackTraceElement : mCurrentThread.getStackTrace()) {

stringBuilder

.append(stackTraceElement.toString())

.append(BlockInfo.SEPARATOR);

}

synchronized (sStackMap) {

if (sStackMap.size() == mMaxEntryCount && mMaxEntryCount > 0) {

sStackMap.remove(sStackMap.keySet().iterator().next());

}

sStackMap.put(System.currentTimeMillis(), stringBuilder.toString());

}

}

這樣,整個blockCanary的執行過程就完畢了。

ANR

當卡頓時間大于一定值之后,將會造成ANR,那么Android系統的ANR是如何檢測出來的呢?其實就是通過Watchdog來實現的,這個Watchdog是一個線程。

public class Watchdog extends Thread {

}

我們主要看一下其中的run方法的實現。

@Override

public void run() {

boolean waitedHalf = false;

while (true) {

final ArrayListblockedCheckers;

final String subject;

final boolean allowRestart;

int debuggerWasConnected = 0;

synchronized (this) {

long timeout = CHECK_INTERVAL;

// Make sure we (re)spin the checkers that have become idle within

// this wait-and-check interval

for (int i=0; i0) {

debuggerWasConnected--;

}

// NOTE: We use uptimeMillis() here because we do not want to increment the time we

// wait while asleep. If the device is asleep then the thing that we are waiting

// to timeout on is asleep as well and won't have a chance to run, causing a false

// positive on when to kill things.

long start = SystemClock.uptimeMillis();

while (timeout > 0) {

if (Debug.isDebuggerConnected()) {

debuggerWasConnected = 2;

}

try {

wait(timeout);

} catch (InterruptedException e) {

Log.wtf(TAG, e);

}

if (Debug.isDebuggerConnected()) {

debuggerWasConnected = 2;

}

timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);

}

在這個run方法中,會開啟一個死循環,主要用于持續檢測ANR

while (true) {

}

通過wait,設置每一次休眠時間,

long start = SystemClock.uptimeMillis();

while (timeout > 0) {

if (Debug.isDebuggerConnected()) {

debuggerWasConnected = 2;

}

try {

wait(timeout);

} catch (InterruptedException e) {

Log.wtf(TAG, e);

}

if (Debug.isDebuggerConnected()) {

debuggerWasConnected = 2;

}

timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);

}

當timeout計算完畢之后,會嘗試獲取當前各個的線程的狀態

final int waitState = evaluateCheckerCompletionLocked();

private int evaluateCheckerCompletionLocked() {

int state = COMPLETED;

for (int i=0; i

public int getCompletionStateLocked() {

if (mCompleted) {

return COMPLETED;

} else {

long latency = SystemClock.uptimeMillis() - mStartTime;

if (latency < mWaitMax/2) {

return WAITING;

} else if (latency < mWaitMax) {

return WAITED_HALF;

}

}

return OVERDUE;

}

一旦有線程等待時間超過了最大等待時間,則表示當前已經有ANR。需要dump此時的堆棧信息。

if (waitState == COMPLETED) {

// The monitors have returned; reset

waitedHalf = false;

continue;

} else if (waitState == WAITING) {

// still waiting but within their configured intervals; back off and recheck

continue;

} else if (waitState == WAITED_HALF) {

if (!waitedHalf) {

// We've waited half the deadlock-detection interval. Pull a stack

// trace and wait another half.

ArrayListpids = new ArrayList();

pids.add(Process.myPid());

ActivityManagerService.dumpStackTraces(true, pids, null, null,

getInterestingNativePids());

waitedHalf = true;

}

continue;

}

此外,還有一個第三方庫,ANRWatchDog,也是用來檢測Anr的,其實原理更加簡單,

public void run() {

setName("|ANR-WatchDog|");

int lastTick;

int lastIgnored = -1;

while (!isInterrupted()) {

lastTick = _tick;

_uiHandler.post(_ticker);

try {

Thread.sleep(_timeoutInterval);

}

catch (InterruptedException e) {

_interruptionListener.onInterrupted(e);

return ;

}

// If the main thread has not handled _ticker, it is blocked. ANR.

if (_tick == lastTick) {

if (!_ignoreDebugger && Debug.isDebuggerConnected()) {

if (_tick != lastIgnored)

Log.w("ANRWatchdog", "An ANR was detected but ignored because the debugger is connected (you can prevent this with setIgnoreDebugger(true))");

lastIgnored = _tick;

continue ;

}

ANRError error;

if (_namePrefix != null)

error = ANRError.New(_namePrefix, _logThreadsWithoutStackTrace);

else

error = ANRError.NewMainOnly();

_anrListener.onAppNotResponding(error);

return;

}

}

}

它會在線程中,利用uiHanlder拋出一個計數器,然后wait指定時間,一旦等待時間到達,那么它會檢查計數的值是否發生改變,如果沒有發生改變,表示uiHandler的計算方法并沒有執行到。也就是出現了Anr,此時需要dump堆棧信息。

總結

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

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