BlockCanary 무엇은
비 간섭 성능 모니터링 구성 요소
BlockCanary의 역할은
발견 UI Caton 문제 (ANR 일반적으로 우리가 정보를 얻을 수있을 때 때 스택)
UI Caton 원리
1.60fps ----> 16m / s 프레임
2 시도는 각각 16m 내에서 처리되도록 / 모든 CPU 및 GPU 그렇지 않으면 발생할 것이다, 도면을 계산하는 렌더링 및 기타 작업 프레임 손실 문제 Caton는이야
메인 쓰레드의 역할
1. 메인 쓰레드의 역할 (응용 프로그램 시작 후, 메인 스레드가 ActivityThread입니다 만들 수)
2. 이벤트 (클릭, 터치 등) 또는 해당보기 위젯에 전송
의 응용 프로그램과도 상호 작용 3. 응용 프로그램 UI 주요 스레드
(소모 동작은 메인 스레드에서 처리 할 수없는) 서브 스레드의 처리 동작에있어서 공통 처리
핸들러
Activity.runOnUiThread (실행 가능)
View.post (실행 가능)
View.postDelayed (실행 가능한 롱)
UI Caton의 일반적인 원인
1. 소모적 작업
2. 레이아웃 레이아웃은 너무 복잡 16m / S 내의 최종 렌더링 될
3.View 과도한 연신
의 측정, 레이아웃 4.View 빈번한 트리거
(5 메모리를 너무 자주 GC 트리거 GC의 가비지 콜렉션을 완료하면 가비지 컬렉션의 구현에 가상 머신, UI 스레드를 포함한 모든 스레드가) 작업을 진행하기 전에 만 모든 스레드를 일시 중지됩니다
에서 봐 [ActivityThread] (안드로이드 응용 프로그램이 하나의 메인 스레드이다가 ActivityThread)는
안드로이드 소스 코드를 웹 사이트 주소를 참조
//创建一个Looper,Looper里又会关联消息队列MessageQueue
final Looper mLooper = Looper.myLooper();
public static void main(String[] args) {
.
.
.
//一个主线程创建MainLooper后,就会在我们的应用生命周期内,不断轮询,通过Looper.loop()方法,获取到消息队列中的Message,然后通知我们的主线程,去更新UI
Looper.prepareMainLooper();
}
prepareMainLooper () 메소드를 봐
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//创建一个主线程的Looper(不管整个应用的主线程,它有多少个子线程,主线程中只有这一个Looper,不管有多少Hanlder,在其它地方可以创建Handler,最终会会到这个sMainLooper 上)
sMainLooper = myLooper();
}
}
//在msg.target.dispatchMessage(msg)方法上下方都有log输出,在dispatchMessage上下分别打印方法执行的时间,根据时间差,来判定dispatchMessage中有没有耗时操作,也就是dispatchMessage中,有没有UI卡顿,如果超过时间差的阈值,就要打印出信息,BlockCanary就是利了这个特点,这也是它的核心实现原理
public static void loop() {
final Printer logging = me.mLogging;
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
//分发message,target就是Handler
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
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);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); .
}
msg.target.dispatchMessage (MSG) 방법을 봐
//这都是执行在主线程中,如果产生了UI卡顿,必定是在这个方法当中
public void dispatchMessage(Message msg) {
//callback就是runnable
if (msg.callback != null) {
//handleCallback,就是把runnable放到方法中,调用它的run方法执行我们的子线程
handleCallback(msg);
} else {
//相当于调用hanlder.sendMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//处理消息
handleMessage(msg);
}
}
봐 BlockCanary.install 방법
public static BlockCanary install(Context context, BlockCanaryContext blockCanaryContext) {
//init方法就做了BlockCanaryContext的成员变量的赋值
BlockCanaryContext.init(context, blockCanaryContext);
//设置通知栏消息是否开启或关闭,BlockCanaryContext.get().displayNotification(),debug模式默开启(true),release默认关闭(false)
setEnabled(context, DisplayActivity.class, BlockCanaryContext.get().displayNotification());
return get();
}
룩 GET () 메소드
public static BlockCanary get() {
//单例,生成BlockCanary的实例
if (sInstance == null) {
synchronized (BlockCanary.class) {
if (sInstance == null) {
sInstance = new BlockCanary();
}
}
}
return sInstance;
}
생성자는 BlockCanary 봐
private BlockCanary() {
BlockCanaryInternals.setContext(BlockCanaryContext.get());
//单例获取
mBlockCanaryCore = BlockCanaryInternals.getInstance();
mBlockCanaryCore.addBlockInterceptor(BlockCanaryContext.get());
if (!BlockCanaryContext.get().displayNotification()) {
return;
}
//通知展示
mBlockCanaryCore.addBlockInterceptor(new DisplayService());
}
모양의 BlockCanaryInternals 건설하는 방법
public BlockCanaryInternals() {
//dump出线程的stack信息,传入的参数是主线程,第二个参数是Dump的间隔
stackSampler = new StackSampler(
Looper.getMainLooper().getThread(),
sContext.provideDumpInterval());
//dump出cpu的有关情况
cpuSampler = new CpuSampler(sContext.provideDumpInterval());
//LooperMonitor是在dispatchMessage上下分别打印方法执行的时间
setMonitor(new LooperMonitor(new LooperMonitor.BlockListener() {
//主线程的调用栈,CPU的是使用情况,内存使用情况都会这个方法监听到,会回调
@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()));
//就是用来删除日志用的,因为在BlockCanary当中,默认情况下日志会保存两天,需要定期的删除
LogWriter.cleanObsolete();
}
봐 시작 () 메소드
public void start() {
if (!mMonitorStarted) {
mMonitorStarted = true;
// Looper.getMainLooper()得到主线程Looper,调用setMessageLogging打印时间 Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor);
}
}
에 println의 룩 LooperMonitor (문자열 X) 방법
//打印时间
public void println(String x) {
if (mStopWhenDebugging && Debug.isDebuggerConnected()) {
return;
}
//表示是否在dispatchMessage方法前
if (!mPrintingStarted) {
//系统时间
mStartTimestamp = System.currentTimeMillis();
//当前线程运行状态的总时间(sleep和wait不会记录到时间里去)
mStartThreadTimestamp = SystemClock.currentThreadTimeMillis();
mPrintingStarted = true;
startDump();
} else { //表示是否在dispatchMessage方法后
final long endTime = System.currentTimeMillis();
mPrintingStarted = false;
//表示有卡顿,UI有阻塞
if (isBlock(endTime)) {
notifyBlockEvent(endTime);
}
stopDump();
}
}
startDump () 메소드를 봐
private void startDump() {
if (null != BlockCanaryInternals.getInstance().stackSampler) {
BlockCanaryInternals.getInstance().stackSampler.start();
}
if (null != BlockCanaryInternals.getInstance().cpuSampler) {
BlockCanaryInternals.getInstance().cpuSampler.start();
}
}
BlockCanaryInternals.getInstance ()를 찾습니다. StackSampler.start () 메소드
public void start() {
if (mShouldSample.get()) {
return;
}
mShouldSample.set(true);
HandlerThreadFactory.getTimerThreadHandler().removeCallbacks(mRunnable);
HandlerThreadFactory.getTimerThreadHandler().postDelayed(mRunnable,
BlockCanaryInternals.getInstance().getSampleDelay());
}
봐 mRunnable는 aabstract 클래스 AbstractSampler 년에, AbstractSampler는 stackSampler 및 cpuSampler 추상 클래스입니다
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
doSample();
if (mShouldSample.get()) {
HandlerThreadFactory.getTimerThreadHandler()
.postDelayed(mRunnable, mSampleInterval);
}
}
};
doSample () StackSampler의 추상적 인 방법을 찾아 구현
피
//打印栈的信息
rotected void doSample() {
StringBuilder stringBuilder = new StringBuilder();
//mCurrentThread.getStackTrace()获取到当前线程的调用栈,mCurrentThread是主线程,因为前面调用的是getMainLooper
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());
}
//以当前时间为Key,放入到HashMap中,sStackMap是LinkedHashMap,这里为什么要定义为LinkedHashMap,LinkedHashMap会记录插入的键的顺序,所以遍历的时候是按插入顺序的,普通的HashMap无法做到,所以遍历顺序未知
sStackMap.put(System.currentTimeMillis(), stringBuilder.toString());
}
}
doSample () CpuSampler의 추상적 인 방법을 찾아 구현
@Override
protected void doSample() {
BufferedReader cpuReader = null;
BufferedReader pidReader = null;
try {
//就是读写
cpuReader = new BufferedReader(new InputStreamReader(
new FileInputStream("/proc/stat")), BUFFER_SIZE);
String cpuRate = cpuReader.readLine();
if (cpuRate == null) {
cpuRate = "";
}
.
.
.
} } catch (Throwable throwable) {}
}
봐 isBlock (endTime- 사용자) 방법
private boolean isBlock(long endTime) {
//endTime是dispatchMessage后的时间,mStartTimestamp 是dispatchMessage前的时间,mBlockThresholdMillis时间为3000,如果大于就表示产生卡顿了,UI阻塞了
return endTime - mStartTimestamp > mBlockThresholdMillis;
}
룩 notifyBlockEvent (endTime- 사용자) 방법
private void notifyBlockEvent(final long endTime) {
final long startTime = mStartTimestamp;
final long startThreadTime = mStartThreadTimestamp;
final long endThreadTime = SystemClock.currentThreadTimeMillis();
HandlerThreadFactory.getWriteLogThreadHandler().post(new Runnable() {
@Override
public void run() {
//调用前面所说的监听事件
mBlockListener.onBlockEvent(startTime, endTime, startThreadTime, endThreadTime);
}
});
}