欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

4章 性能平台GodEye源码分析-监控模块

程序员文章站 2022-03-12 21:33:33
4. 监控模块4.1 PageloadPageload指的是监听应用Activity和Fragment的生命周期,通过记录当前的生命周期追踪用户的页面跳转行为源码分析1、启动Pageload的监控Pageload的监控通过系统回调的生命周期,通过ActivityLifecycleCallbacks.work()注册系统的生命周期,进而通过回调获取Pageload信息public class Pageload extends ProduceableSubject

4. 监控模块

4.1 Pageload

4章 性能平台GodEye源码分析-监控模块

Pageload指的是监听应用Activity和Fragment的生命周期,通过记录当前的生命周期追踪用户的页面跳转行为

源码分析

1、启动Pageload的监控

Pageload的监控通过系统回调的生命周期,通过ActivityLifecycleCallbacks.work()注册系统的生命周期,进而通过回调获取Pageload信息

public class Pageload extends ProduceableSubject<PageLifecycleEventInfo> implements Install<PageloadConfig> {
    private static final String PAGELOAD_HANDLER = "godeye-pageload";
    private ActivityLifecycleCallbacks mActivityLifecycleCallbacks;
    private PageloadConfig mConfig;
    private boolean mInstalled = false;

    @Override
    public synchronized boolean install(PageloadConfig config) {
        if (mInstalled) {
            L.d("Pageload already installed, ignore.");
            return true;
        }
        this.mConfig = config;
        PageInfoProvider pageInfoProvider = new DefaultPageInfoProvider();
        try {
            pageInfoProvider = (PageInfoProvider) Class.forName(this.mConfig.pageInfoProvider()).newInstance();
        } catch (Throwable e) {
            L.e("Pageload install warning, can not find pageload provider class. use DefaultPageInfoProvider:" + e);
        }
        Handler handler = ThreadUtil.createIfNotExistHandler(PAGELOAD_HANDLER);
        this.mActivityLifecycleCallbacks = new ActivityLifecycleCallbacks(new PageLifecycleRecords(), pageInfoProvider, this, handler);
        this.mActivityLifecycleCallbacks.work();
        this.mInstalled = true;
        L.d("Pageload installed.");
        return true;
    }
}

2、采集Pageload信息

通过Application注册当前的生命周期回调,实现ActivityLifecycleCallbacks接口,当App启动或跳转页面时,会回调对应的生命周期

@Override
public void work() {
    GodEye.instance().getApplication().registerActivityLifecycleCallbacks(this);
}

onActivityCreated回调中,大于AndroidO则可以注册当前界面的Fragment回调,或者是FragmentActivity也能注册

@Override
public void onActivityCreated(final Activity activity, Bundle savedInstanceState) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        activity.getFragmentManager().registerFragmentLifecycleCallbacks(new FragmentLifecycleCallbacks(ActivityLifecycleCallbacks.this), true);
    }
    if (activity instanceof FragmentActivity) {
        ((FragmentActivity) activity).getSupportFragmentManager().registerFragmentLifecycleCallbacks(new FragmentLifecycleCallbacksV4(ActivityLifecycleCallbacks.this), true);
    }
    onActivityLifecycleEvent(activity, ActivityLifecycleEvent.ON_CREATE, false);
}

onActivityStarted回调中,在这里会处理计算当前界面的OnDraw时间,如果处理过则加入到mStartedActivity集合中

@Override
public void onActivityStarted(final Activity activity) {
    onActivityLifecycleEvent(activity, ActivityLifecycleEvent.ON_START, false);
    if (!mStartedActivity.contains(activity)) {
        mStartedActivity.add(activity);
        ViewUtil.measureActivityDidDraw(activity, () -> {
            onActivityLifecycleEvent(activity, ActivityLifecycleEvent.ON_DRAW, false);
        });
    }
}

处理onDraw耗时的原理是:

  1. 通过在ActivityStart的时候监听通知DecorView.getViewTreeObserver().addOnDrawListener
  2. 通过postTraversalFinishCallBack执行ChoreographerpostCallback()方法,等待系统的Vsync回调
  3. 在Vsync回调中,如果已经执行过onDraw回调则直接回调到最外层,如果未执行过onDraw回调则重试,最多3次
//ViewUtil.java
public static void measureActivityDidDraw(final Activity activity, final OnDrawCallback onDrawCallback) {
    measurePageDidDraw(activity.getWindow().getDecorView(), onDrawCallback);
}

private static void measurePageDidDraw(final View view, final OnDrawCallback onDrawCallback) {
    PageDrawMonitor.newInstance(view, onDrawCallback).listen();
}

public void listen() {
    ViewTreeObserver.OnDrawListener onDrawListener = () -> isDraw = true;
    view.getViewTreeObserver().addOnDrawListener(onDrawListener); //监听通知
    runOnDrawEnd(view, onDrawListener, 3, onDrawCallback); //执行回调
}

private void runOnDrawEnd(View view, ViewTreeObserver.OnDrawListener onDrawListener, int maxPostTimes, @NonNull ViewUtil.OnDrawCallback onDrawCallback) {
    if (view == null || onDrawListener == null) {
        return;
    }
    maxPostTimes --;
    int finalMaxPostTimes = maxPostTimes;
    postTraversalFinishCallBack(() -> {
        if (!isDraw && finalMaxPostTimes > 0) { //isDraw没被回调,则重试
            runOnDrawEnd(view, onDrawListener, finalMaxPostTimes, onDrawCallback);
        } else { //isDraw被回调,移除并回调到最外层
            view.getViewTreeObserver().removeOnDrawListener(onDrawListener);
            onDrawCallback.didDraw();
        }
    });
}

private void postTraversalFinishCallBack(OnTraversalFinishListener onTraversalFinishListener) {
    if (choreographerMethod == null) {
        return;
    }
    try {
        choreographerMethod.invoke(ChoreographerInjecor.getChoreographerProvider().getChoreographer(), CALLBACK_COMMIT, (Runnable) () -> {
            if (onTraversalFinishListener != null) {
                onTraversalFinishListener.onFinish();
            }
        }, FRAME_CALLBACK_TOKEN);
    } catch (Throwable throwable) {
        L.w(throwable.getMessage());
    }
}

同样的在其他回调中,可以看到最终都会回调onActivityLifecycleEvent()

@Override
public void onActivityResumed(final Activity activity) {
    onActivityLifecycleEvent(activity, ActivityLifecycleEvent.ON_RESUME, false);
}

@Override
public void onActivityPaused(final Activity activity) {
    onActivityLifecycleEvent(activity, ActivityLifecycleEvent.ON_PAUSE, false);
}

@Override
public void onActivityStopped(final Activity activity) {
    onActivityLifecycleEvent(activity, ActivityLifecycleEvent.ON_STOP, false);
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

}

@Override
public void onActivityDestroyed(final Activity activity) {
    onActivityLifecycleEvent(activity, ActivityLifecycleEvent.ON_DESTROY, false);
    mStartedActivity.remove(activity);
}

onActivityLifecycleEvent()中,对当前的生命周期做记录和采集,其中步骤分为

  1. new PageInfo():初始化数据对象
  2. mPageLifecycleRecords.isExistEvent():判断当前生命周期对象是否已经记录下来
  3. mPageLifecycleRecords.addLifecycleEvent():将当前生命周期对象进行记录
  4. mProducer.produce():采集当前的生命周期对象信息
private void onActivityLifecycleEvent(Activity activity, LifecycleEvent e, boolean canNotRepeat) {
    final long time = System.currentTimeMillis();
    mHandler.post(() -> {
        PageInfo<Activity> pageInfo = new PageInfo<>(activity, mPageInfoProvider.getInfoByActivity(activity));
        if (canNotRepeat && mPageLifecycleRecords.isExistEvent(pageInfo, e)) {
            return;
        }
        PageLifecycleEventWithTime<Activity> lifecycleEvent = mPageLifecycleRecords.addLifecycleEvent(pageInfo, e, time);
        if (lifecycleEvent != null) {
            mProducer.produce(new PageLifecycleEventInfo<>(pageInfo, lifecycleEvent, mPageLifecycleRecords.getLifecycleEventsByPageInfo(pageInfo)));
        }
    });
}

PageInfo对象初始化的时候,就已经把对应的字段赋值成对应的值,其中参数extraInfo就是从xml配置文件中读取的参数值,作为额外的标注信息

public PageInfo(T page, Map<String, String> extraInfo) {
    if (page instanceof Activity) {
        this.pageType = PageType.ACTIVITY;
    } else if (page instanceof Fragment || page instanceof android.app.Fragment) {
        this.pageType = PageType.FRAGMENT;
    } else {
        this.pageType = PageType.UNKNOWN;
    }
    this.pageClassName = page.getClass().getName();
    this.pageHashCode = page.hashCode();
    this.extraInfo = extraInfo;
}

在记录和采集之前通过isExistEvent()判断下是否已经处理过当前的生命周期对象,如果判断是处理过的对象则返回true

synchronized boolean isExistEvent(PageInfo pageInfo, LifecycleEvent lifecycleEvent) {
    List<PageLifecycleEventWithTime> pageLifecycleEventWithTimes = getLifecycleEventsByPageInfo(pageInfo);
    if (pageLifecycleEventWithTimes != null && !pageLifecycleEventWithTimes.isEmpty()) {
        for (PageLifecycleEventWithTime pageLifecycleEventWithTime : pageLifecycleEventWithTimes) {
            if (pageLifecycleEventWithTime.lifecycleEvent == lifecycleEvent) {
                return true;
            }
        }
    }
    return false;
}

判断未处理的事件,则会通过mPageLifecycleRecords.addLifecycleEvent()将当前处理过的记录存储起来

  1. 如果是非系统的生命周期则直接新增到集合中
  2. 如果是系统的生命周期,则访问mTopLifecycleMethodEventForPages是否有对象,如果没有则直接新增到集合中
  3. mTopLifecycleMethodEventForPages是属于MethodCanary的过程中加入的监控对象,正常情况会为null
synchronized @Nullable
<T> PageLifecycleEventWithTime<T> addLifecycleEvent(PageInfo<T> pageInfo, LifecycleEvent lifecycleEvent, long time) {
    if (!isSystemLifecycleEvent(lifecycleEvent)) {
        return addEvent(pageInfo, new PageLifecycleEventWithTime<>(pageInfo, lifecycleEvent, time, time));
    }
    LifecycleMethodEventWithTime currentTop = mTopLifecycleMethodEventForPages.get(pageInfo);
    PageLifecycleEventWithTime<T> tmp = null;
    if (currentTop == null) {
        tmp = new PageLifecycleEventWithTime<>(pageInfo, lifecycleEvent, time, time);
    } else if (!PageLifecycleMethodEventTypes.isMatch(lifecycleEvent, currentTop.lifecycleEvent)) {
        tmp = new PageLifecycleEventWithTime<>(pageInfo, lifecycleEvent, time, time);
    } else if (currentTop.methodStartTime > 0 && currentTop.methodEndTime > 0) {
        tmp = new PageLifecycleEventWithTime<>(pageInfo, lifecycleEvent, currentTop.methodStartTime, time);
    }
    if (tmp != null) {
        mTopLifecycleMethodEventForPages.remove(pageInfo);
        return addEvent(pageInfo, tmp);
    } else {
        currentTop.lifecycleTime = time;
        return null;
    }
}

addEvent()会将当前的事件,添加到当前声明过的数据结构中,其中

  • mPageLifecycleEventIndexing:保存着所有页面,其页面对应的生命周期事件的索引列表
  • mLifecycleEvents:保存所有页面的生命周期,可以通过mPageLifecycleEventIndexing中的索引列表来这里取对应的值
  • mTopLifecycleMethodEventForPages:保存着所有页面,当前的*生命周期事件
private Map<PageInfo, List<Integer>> mPageLifecycleEventIndexing = new HashMap<>();
private List<PageLifecycleEventWithTime> mLifecycleEvents = new ArrayList<>();
private Map<PageInfo, LifecycleMethodEventWithTime> mTopLifecycleMethodEventForPages = new HashMap<>();

private <T> PageLifecycleEventWithTime<T> addEvent(PageInfo<T> pageInfo, PageLifecycleEventWithTime<T> pageLifecycleEventLine) {
    //每次新增都会增加事件到当前集合,该集合维护所有页面的所有生命周期事件
    mLifecycleEvents.add(pageLifecycleEventLine); 
    //每个页面维护着所有生命周期在mLifecycleEvents中的索引
    List<Integer> pageEventIndexingList = mPageLifecycleEventIndexing.get(pageInfo); 
    if (pageEventIndexingList == null) {
        pageEventIndexingList = new ArrayList<>();
        //如果当前界面还未保存过,则新增个界面信息,mPageLifecycleEventIndexing保存着每个界面的所有生命周期的参数
        mPageLifecycleEventIndexing.put(pageInfo, pageEventIndexingList); 
    }
    pageEventIndexingList.add(mLifecycleEvents.size() - 1);
    return pageLifecycleEventLine;
}

看完addEvent()方法后,知道了其保存的数据结构,那么getLifecycleEventsByPageInfo()中,相当于去解析当前的数据结构,获取想要的数据结果

  1. 获取当前生命周期的index位置
  2. 通过index去获取当前页面的生命周期对象
  3. 将属于当前页面的生命周期对象加入到新的集合中返回

synchronized List<PageLifecycleEventWithTime> getLifecycleEventsByPageInfo(PageInfo pageInfo) {
    List<Integer> pageEventIndexingList = mPageLifecycleEventIndexing.get(pageInfo); //获取当前页面的生命周期,在整个集合中的索引位置
    if (pageEventIndexingList == null) {
        return new ArrayList<>();
    }
    List<PageLifecycleEventWithTime> pageLifecycleEventWithTimes = new ArrayList<>(pageEventIndexingList.size());
    for (int index : pageEventIndexingList) {
        pageLifecycleEventWithTimes.add(mLifecycleEvents.get(index)); //通过索引位置获取整个集合属于当前页面的生命周期对象
    }
    return pageLifecycleEventWithTimes;
}

3、总结

Activity的生命周期的获取和采集作者做了额外的非系统生命周期,会去计算界面draw的时间,对于Fragment的生命周期的获取和采集,和Activity的原理相同

4.2 ViewCanary

4章 性能平台GodEye源码分析-监控模块

ViewCanary会通过代码去扫描当前界面的层级,遍历并采集所有View的属性和信息

源码分析

1、启动ViewCanary的监控

ViewCanary通过系统回调的生命周期,通过ViewCanaryInternal获取ViewCanary信息

public class ViewCanary extends ProduceableSubject<ViewIssueInfo> implements Install<ViewCanaryConfig> {

    private ViewCanaryConfig config;
    private boolean mInstalled = false;
    private ViewCanaryInternal mViewCanaryInternal;

    @Override
    public synchronized boolean install(ViewCanaryConfig config) {
        if (mInstalled) {
            L.d("ViewCanary already installed, ignore.");
            return true;
        }
        this.config = config;
        mViewCanaryInternal = new ViewCanaryInternal();
        mViewCanaryInternal.start(this, config());
        mInstalled = true;
        L.d("ViewCanary installed.");
        return true;
    }
}

2、采集ViewCanary信息

  • 通过mViewCanaryInternal.start()调用registerActivityLifecycleCallbacks()注册Activity的生命周期广播通知
  • 在每个Activity启动的时候,在onActivityCreated周期中,记录扫过的界面,在onActivityDestroyed周期中,移除扫过的界面
  • 在每个Activity启动的时候,在onActivityResumed周期中,开启界面的扫描,在onActivityPaused周期中,停止界面的扫描
void start(ViewCanary viewCanary, ViewCanaryConfig config) {
    Handler handler = ThreadUtil.createIfNotExistHandler(VIEW_CANARY_HANDLER);
    callbacks = new SimpleActivityLifecycleCallbacks() {

        private Map<Activity, ViewTreeObserver.OnGlobalLayoutListener> mOnGlobalLayoutListenerMap = new HashMap<>();
        private Map<Activity, List<List<ViewIdWithSize>>> mRecentLayoutListRecords = new HashMap<>();

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            super.onActivityCreated(activity, savedInstanceState);
            mRecentLayoutListRecords.put(activity, new ArrayList<>());
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
            super.onActivityDestroyed(activity);
            mRecentLayoutListRecords.remove(activity);
        }

        @Override
        public void onActivityResumed(Activity activity) {
            super.onActivityResumed(activity);
            ViewGroup parent = (ViewGroup) activity.getWindow().getDecorView();
            Runnable callback = inspectInner(new WeakReference<>(activity), viewCanary, config, mRecentLayoutListRecords);
            ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = () -> {
                handler.removeCallbacks(callback);
                handler.postDelayed(callback, INSPECT_DELAY_TIME_MILLIS);
            };
            mOnGlobalLayoutListenerMap.put(activity, onGlobalLayoutListener);
            parent.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
        }

        @Override
        public void onActivityPaused(Activity activity) {
            super.onActivityPaused(activity);
            ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = mOnGlobalLayoutListenerMap.remove(activity);
            ViewGroup parent = (ViewGroup) activity.getWindow().getDecorView();
            if (onGlobalLayoutListener != null) {
                parent.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
            }
        }
    };
    GodEye.instance().getApplication().registerActivityLifecycleCallbacks(callbacks);
}

在界面启动时,回调onActivityResumed

  • 声明界面扫描任务的callback,通过inspectInner类去封装逻辑
  • 当界面绘制完成后,GlobalLayoutListener会回调,将callback分发给Handler去执行
@Override
public void onActivityResumed(Activity activity) {
    super.onActivityResumed(activity);
    ViewGroup parent = (ViewGroup) activity.getWindow().getDecorView();
    Runnable callback = inspectInner(new WeakReference<>(activity), viewCanary, config, mRecentLayoutListRecords);
    ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = () -> {
        handler.removeCallbacks(callback);
        handler.postDelayed(callback, INSPECT_DELAY_TIME_MILLIS);
    };
    mOnGlobalLayoutListenerMap.put(activity, onGlobalLayoutListener);
    parent.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}

Handler会去执行callbackrun()回调,真正执行界面的扫描工作,其原理是:

  1. 通过rootview获取所有子View和子View的层级数,加入到Map中
  2. 获取每个View的宽高并记录下来,由于采集显示的页面需要展示视图位置和大小
  3. 检查并统计每个View的过度位置信息
  4. 构建问题View对象,将所有信息赋值后,发送出去
@VisibleForTesting
Runnable inspectInner(WeakReference<Activity> activity, ViewCanary viewCanary, ViewCanaryConfig config, Map<Activity, List<List<ViewIdWithSize>>> recentLayoutListRecords) {
    return () -> {
        try {
            Activity p = activity.get();
            if (p != null) {
                ViewGroup parent = (ViewGroup) p.getWindow().getDecorView();
                inspect(p, parent, viewCanary, config, recentLayoutListRecords);
            }
        } catch (Throwable e) {
            L.e(e);
        }
    };
}

private void inspect(Activity activity, ViewGroup root, ViewCanary viewCanary, ViewCanaryConfig config, Map<Activity, List<List<ViewIdWithSize>>> recentLayoutListRecords) {

    //1、将所有View捕捉下来,待检查是否过度绘制
    long startTime = System.currentTimeMillis();
      
    List<List<ViewIdWithSize>> records = recentLayoutListRecords.get(activity);
    if (records == null) { //如果已经检查过当前Activity的,则直接忽略
        return;
    }
    
    Map<View, Integer> depthMap = new HashMap<>();
    List<View> allViews = new ArrayList<>();
    allViews.add(root);
    recursiveLoopChildren(root, depthMap, allViews); //循环遍历所有View,并记录所有View的层级数到depthMap
    List<ViewIdWithSize> layoutEigenvalue = getLayoutEigenvalue(root, allViews); //获取所有View的宽高
    boolean allNotSimilar = allNotSimilarByMeasureDistanceLayoutEigenvalueWithRecords(records, layoutEigenvalue);
    
    //2、检查过度绘制的视图信息
    Map<Rect, Set<Object>> overDrawMap = checkOverDraw(allViews);
    
    //3、构建问题View的对象
    ViewIssueInfo info = new ViewIssueInfo();
    int[] resolution = getResolution(); 
    info.activityName = activity.getClass().getName(); //记录Activity参数
    info.timestamp = System.currentTimeMillis(); //记录时间戳参数
    info.maxDepth = config.maxDepth(); //记录max层级参数
    info.screenWidth = resolution[0]; //记录获取屏幕信息
    info.screenHeight = resolution[1]; //记录获取屏幕信息
    for (Map.Entry<View, Integer> entry : depthMap.entrySet()) { //记录所有View参数
        info.views.add(getViewInfo(entry.getKey(), entry.getValue())); 
    }
    for (Map.Entry<Rect, Set<Object>> entry : overDrawMap.entrySet()) {
        ViewIssueInfo.OverDrawArea overDrawArea = new ViewIssueInfo.OverDrawArea();
        overDrawArea.rect = entry.getKey();
        overDrawArea.overDrawTimes = getOverDrawTimes(entry.getValue()) - 1;
        info.overDrawAreas.add(overDrawArea); //记录过度绘制层数参数对象
    }
    records.add(layoutEigenvalue);
    viewCanary.produce(info);
    L.d("ViewCanary inspect %s complete, cost %sms.", activity.getClass().getSimpleName(), (System.currentTimeMillis() - startTime));
}

通过checkOverDraw实现检查过度绘制的区域,并记录下来,其原理是

  • 通过两次循环遍历当前View和所有View相交部分的区域
  • 通过两次循环遍历当前相交部分的区域视图,和所有相交部分的区域视图,再做一层相交处理
  • 通过这两个操作后,就算出了相交一次和相交两次后的所有视图区域信息
private Map<Rect, Set<Object>> checkOverDraw(List<View> allViews) {
    Map<Rect, Set<Object>> map = new HashMap<>();
    //两次循环遍历当前View和所有View
    for (int i = 0; i < allViews.size(); i++) {
        View view = allViews.get(i);
        for (int j = i + 1; j < allViews.size(); j++) {
            View other = allViews.get(j);
            int viewLayer = ViewBgUtil.getLayer(view);
            int otherLayer = ViewBgUtil.getLayer(other);
            if (view == other || viewLayer == 0 || otherLayer == 0) {
                continue;
            }
            Rect r1 = new Rect();
            Rect r2 = new Rect();
            getViewRect(view, r1);
            getViewRect(other, r2);
            //如果没相交则继续,相交则下一步
            if (!Rect.intersects(r1, r2)) {
                continue;
            }
            //计算相交部分的区域
            Rect overDraw = calculateOverDraw(r1, r2);
            Set<Object> set;
            if (map.containsKey(overDraw)) {
                set = map.get(overDraw);
            } else {
                set = new HashSet<>();
            }
            //保存相交的区域
            set.add(view);
            set.add(other);
            map.put(overDraw, set);
        }
    }

    Map<Rect, Set<Object>> rest = new HashMap<>();

    //两次循环遍历当前相交部分的区域视图,和所有相交部分的区域视图
    for (Map.Entry<Rect, Set<Object>> entry : map.entrySet()) {
        Rect rect = entry.getKey();
        for (Map.Entry<Rect, Set<Object>> otherEntry : map.entrySet()) {
            Rect otherRect = otherEntry.getKey();
            //如果没相交则继续,相交则下一步
            if (rect == otherRect || !Rect.intersects(rect, otherRect)) {
                continue;
            }
            Rect overDraw = calculateOverDraw(rect, otherRect);
            if (map.containsKey(overDraw)) {
                continue;
            }
            Set<Object> set;
            if (rest.containsKey(overDraw)) {
                set = rest.get(overDraw);
            } else {
                set = new HashSet<>();
            }
            //保存相交的区域
            set.add(otherRect);
            rest.put(overDraw, set);
        }
    }
    map.putAll(rest);
    return map;
}

如何知道相交的部分,则通过calculateOverDraw,画个相交的矩形,对着代码看就知道什么意思了

private Rect calculateOverDraw(Rect r1, Rect r2) {
    Rect overDrawArea = new Rect();
    overDrawArea.left = Math.max(r1.left, r2.left);
    overDrawArea.right = Math.min(r1.right, r2.right);
    overDrawArea.bottom = Math.min(r1.bottom, r2.bottom);
    overDrawArea.top = Math.max(r1.top, r2.top);
    return overDrawArea;
}

3、总结

ViewCanary的思维比较简单,计算方式比较多,只要有同样的思路,用代码表达出来就可以了,简单的说就是遍历View树,再结合View的属性来完成视图的计算工作

4.3 ImageCanary

4章 性能平台GodEye源码分析-监控模块

ImageCanary会通过代码去扫描当前界面的所有图片,通过自定义问题图片的规则,过滤出有问题的图片

源码分析

1、启动ImageCanary的监控

ImageCanary通过系统回调的生命周期,通过ImageCanaryInternal获取ImageCanary信息

public class ImageCanary extends ProduceableSubject<ImageIssue> implements Install<ImageCanaryConfig> {

    private boolean mInstalled = false;
    private ImageCanaryConfig mConfig;
    private ImageCanaryInternal mImageCanaryInternal;

    @Override
    public synchronized boolean install(ImageCanaryConfig config) {
        if (mInstalled) {
            L.d("ImageCanary already installed, ignore.");
            return true;
        }
        mConfig = config;
        ImageCanaryConfigProvider imageCanaryConfigProvider = new DefaultImageCanaryConfigProvider();
        try {
            imageCanaryConfigProvider = (ImageCanaryConfigProvider) Class.forName(mConfig.getImageCanaryConfigProvider()).newInstance();
        } catch (Throwable e) {
            L.e("ImageCanary install warning, can not find imageCanaryConfigProvider class. use DefaultImageCanaryConfigProvider:" + e);
        }
        mImageCanaryInternal = new ImageCanaryInternal(imageCanaryConfigProvider);
        mImageCanaryInternal.start(GodEye.instance().getApplication(), this);
        mInstalled = true;
        L.d("ImageCanary installed.");
        return true;
    }
}

其中DefaultImageCanaryConfigProvider属于问题图片的规则定义,其规则如下

  • Bitmap质量过大:图片的尺寸大于ImageView的尺寸的1.5倍
  • Bitmap质量过小:图片的尺寸的2倍小于ImageView的尺寸
public class DefaultImageCanaryConfigProvider implements ImageCanaryConfigProvider {

    @Override
    public boolean isBitmapQualityTooHigh(int bitmapWidth, int bitmapHeight, int imageViewWidth, int imageViewHeight) {
        return bitmapWidth * bitmapHeight > imageViewWidth * imageViewHeight * 1.5;
    }

    @Override
    public boolean isBitmapQualityTooLow(int bitmapWidth, int bitmapHeight, int imageViewWidth, int imageViewHeight) {
        return bitmapWidth * bitmapHeight * 2 < imageViewWidth * imageViewHeight;
    }
}

2、采集ImageCanary信息

  • 通过ImageCanaryInternal.start()调用registerActivityLifecycleCallbacks()注册Activity的生命周期广播通知
  • 在每个Activity启动的时候,在onActivityResumed周期中,通过监听addOnDrawListener扫描界面上所有问题图片
  • 在每个Activity启动的时候,在onActivityPaused周期中,移除监听removeOnDrawListener
void start(Application application, ImageCanary imageCanaryEngine) {
    Handler handler = ThreadUtil.createIfNotExistHandler(IMAGE_CANARY_HANDLER);
    callbacks = new SimpleActivityLifecycleCallbacks() {

        private Map<Activity, ViewTreeObserver.OnDrawListener> mOnDrawListenerMap = new HashMap<>();
        private Set<ImageIssue> mImageIssues = new HashSet<>();

        @Override
        public void onActivityResumed(Activity activity) {
            super.onActivityResumed(activity);
            ViewGroup parent = (ViewGroup) activity.getWindow().getDecorView();
            Runnable callback = inspectInner(new WeakReference<>(activity), imageCanaryEngine, mImageIssues);
            ViewTreeObserver.OnDrawListener onDrawListener = () -> {
                if (handler != null) {
                    handler.removeCallbacks(callback);
                    handler.postDelayed(callback, 300);
                }
            };
            mOnDrawListenerMap.put(activity, onDrawListener);
            parent.getViewTreeObserver().addOnDrawListener(onDrawListener);
        }

        @Override
        public void onActivityPaused(Activity activity) {
            super.onActivityPaused(activity);
            ViewTreeObserver.OnDrawListener onDrawListener = mOnDrawListenerMap.remove(activity);
            ViewGroup parent = (ViewGroup) activity.getWindow().getDecorView();
            if (onDrawListener != null) {
                parent.getViewTreeObserver().removeOnDrawListener(onDrawListener);
            }
        }
    };
    application.registerActivityLifecycleCallbacks(callbacks);
}

扫描问题图片逻辑跟ViewCanary基本一致

  • ViewTreeObserver.OnDrawListener回调的时候,会将callback交给Handler去处理
  • Handler启动callbackrun(),也就是inspectInner里面,执行recursiveLoopChildren
  • 获取DecorView遍历整个View树的BitmapDrawable匹配问题图片的规则
  • 将问题图片封装好的对象,发送出去
Runnable inspectInner(WeakReference<Activity> activity, ImageCanary imageCanaryEngine, Set<ImageIssue> imageIssues) {
    return () -> {
        try {
            Activity p = activity.get();
            if (p != null) {
                ViewGroup parent = (ViewGroup) p.getWindow().getDecorView();
                recursiveLoopChildren(p, parent, imageCanaryEngine, imageIssues);
            }
        } catch (Throwable e) {
            L.e(e);
        }
    };
}

private void recursiveLoopChildren(Activity activity, ViewGroup parent, ImageCanary imageCanaryEngine, Set<ImageIssue> imageIssues) {
    ViewUtil.getChildren(parent, view -> false, view -> {
        List<BitmapInfo> bitmapInfos = mBitmapInfoAnalyzer.analyze(view);
        for (BitmapInfo bitmapInfo : bitmapInfos) {
            if (bitmapInfo.isValid()) {
                ImageIssue imageIssue = new ImageIssue();
                imageIssue.bitmapHeight = bitmapInfo.bitmapHeight;
                imageIssue.bitmapWidth = bitmapInfo.bitmapWidth;
                imageIssue.imageViewHashCode = view.hashCode();
                imageIssue.imageViewWidth = view.getWidth();
                imageIssue.imageViewHeight = view.getHeight();
                imageIssue.activityClassName = activity.getClass().getName();
                imageIssue.activityHashCode = activity.hashCode();
                imageIssue.timestamp = System.currentTimeMillis();
                if (view.getVisibility() != View.VISIBLE) {
                    imageIssue.issueType = ImageIssue.IssueType.INVISIBLE_BUT_MEMORY_OCCUPIED;
                } else if (mImageCanaryConfigProvider.isBitmapQualityTooHigh(bitmapInfo.bitmapWidth, bitmapInfo.bitmapHeight, view.getWidth(), view.getHeight())) {
                    imageIssue.issueType = ImageIssue.IssueType.BITMAP_QUALITY_TOO_HIGH;
                } else if (mImageCanaryConfigProvider.isBitmapQualityTooLow(bitmapInfo.bitmapWidth, bitmapInfo.bitmapHeight, view.getWidth(), view.getHeight())) {
                    imageIssue.issueType = ImageIssue.IssueType.BITMAP_QUALITY_TOO_LOW;
                } else {
                    imageIssue.issueType = ImageIssue.IssueType.NONE;
                }
                if (imageIssue.issueType != ImageIssue.IssueType.NONE && !imageIssues.contains(imageIssue)) {
                    imageIssues.add(new ImageIssue(imageIssue));
                    imageIssue.imageSrcBase64 = ImageUtil.convertToBase64(bitmapInfo.bitmap.get(), 200, 200);
                    imageCanaryEngine.produce(imageIssue);
                }
            }
        }
    });
}

通过ViewUtil.getChildren遍历整个子树图,将每个View交给DefaultBitmapInfoAnalyzer进行分析,匹配BitmapDrawable

public class DefaultBitmapInfoAnalyzer implements BitmapInfoAnalyzer {

@Override
public List<BitmapInfo> analyze(View view) {
    BitmapInfo ivBitMapInfo = null;
    if (view instanceof ImageView) {
        Drawable drawable = ((ImageView) view).getDrawable();
        if (drawable instanceof BitmapDrawable) {
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
            ivBitMapInfo = new BitmapInfo();
            ivBitMapInfo.bitmapWidth = bitmap.getWidth();
            ivBitMapInfo.bitmapHeight = bitmap.getHeight();
            ivBitMapInfo.bitmap = new WeakReference<>(bitmap);
        }
    }
    BitmapInfo vBitMapInfo;
    Drawable drawable = view.getBackground();
    if (drawable instanceof BitmapDrawable) {
        Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
        vBitMapInfo = new BitmapInfo();
        vBitMapInfo.bitmapWidth = bitmap.getWidth();
        vBitMapInfo.bitmapHeight = bitmap.getHeight();
        vBitMapInfo.bitmap = new WeakReference<>(bitmap);
        if (ivBitMapInfo != null) {
            return Arrays.asList(ivBitMapInfo, vBitMapInfo);
        }
        return Arrays.asList(vBitMapInfo);
    }
    if (ivBitMapInfo != null) {
        return Arrays.asList(ivBitMapInfo);
    }
    return Collections.emptyList();
}

3、总结

ImageCanary原理并不难,对View树的遍历,思想和ViewCanary基本一致

4.4 MethodCanary

4章 性能平台GodEye源码分析-监控模块

MethodCanary用的是作者的另一个框架,通过调用开始和结束,采集当前时间段内线程中执行的函数和时间线

源码分析

1、启动MethodCanary的监控

MethodCanary的启动没做任何事情,因为其采集是需要手动调用才可以

public class MethodCanary extends ProduceableSubject<MethodsRecordInfo> implements Install<MethodCanaryConfig> {
    private boolean mInstalled = false;
    private MethodCanaryConfig mMethodCanaryContext;

    @Override
    public synchronized boolean install(final MethodCanaryConfig methodCanaryContext) {
        if (this.mInstalled) {
            L.d("MethodCanary already installed, ignore.");
            return true;
        }
        this.mMethodCanaryContext = methodCanaryContext;
        this.mInstalled = true;
        L.d("MethodCanary installed.");
        return true;
    }
}

2、采集MethodCanary信息

采集的过程使用MethodCanary提供的Api,需要手动调用启动和结束,结束后将返回的方法信息发送出去

public synchronized void startMonitor(String tag) {
    try {
        if (!isInstalled()) {
            L.d("MethodCanary start monitor fail, not installed.");
            return;
        }
        cn.hikyson.methodcanary.lib.MethodCanary.get().startMethodTracing(tag);
        L.d("MethodCanary start monitor success.");
    } catch (Exception e) {
        L.d("MethodCanary start monitor fail:" + e);
    }
}

public synchronized void stopMonitor(String tag) {
    try {
        if (!isInstalled()) {
            L.d("MethodCanary stop monitor fail, not installed.");
            return;
        }
        cn.hikyson.methodcanary.lib.MethodCanary.get().stopMethodTracing(tag
                , new cn.hikyson.methodcanary.lib.MethodCanaryConfig(this.mMethodCanaryContext.lowCostMethodThresholdMillis()), (sessionTag, startMillis, stopMillis, methodEventMap) -> {
                    long start0 = System.currentTimeMillis();
                    MethodsRecordInfo methodsRecordInfo = MethodCanaryConverter.convertToMethodsRecordInfo(startMillis, stopMillis, methodEventMap);
//                        recordToFile(methodEventMap, methodsRecordInfo);
                    long start1 = System.currentTimeMillis();
                    MethodCanaryConverter.filter(methodsRecordInfo, this.mMethodCanaryContext);
                    long end = System.currentTimeMillis();
                    L.d(String.format("MethodCanary output success! cost %s ms, filter cost %s ms", end - start0, end - start1));
                    produce(methodsRecordInfo);
                });
        L.d("MethodCanary stopped monitor and output processing...");
    } catch (Exception e) {
        L.d("MethodCanary stop monitor fail:" + e);
    }
}

作者也是通过Web点击开始和结束操作当前的MethodCanary

public class WebSocketMethodCanaryProcessor implements WebSocketProcessor {
    @Override
    public void process(WebSocket webSocket, JSONObject msgJSONObject) {
        try {
            if ("start".equals(msgJSONObject.optString("payload"))) {
                GodEyeHelper.startMethodCanaryRecording("AndroidGodEye-Monitor-Tag");
            } else if ("stop".equals(msgJSONObject.optString("payload"))) {
                GodEyeHelper.stopMethodCanaryRecording("AndroidGodEye-Monitor-Tag");
            }
            webSocket.send(new ServerMessage("methodCanaryMonitorState", Collections.singletonMap("isRunning", GodEyeHelper.isMethodCanaryRecording("AndroidGodEye-Monitor-Tag"))).toString());
        } catch (UninstallException e) {
            L.e(String.valueOf(e));
        }
    }
}

3、MethodCanary源码分析

原理待后面补齐

4、总结

MethodCanary主要是依赖于作者的第三方库,原理后续推出

4.5 Sm(BlockCanary)

4章 性能平台GodEye源码分析-监控模块

Sm主要是分析当前主线程是否阻塞,阻塞的话其堆栈又是哪些。Sm主要是利用第三方框架BlockCanary的原理,由于Handler每次执行主线程都会通过Printer输出当前主线程的调用时间,系统Api提供设置Printer的方法,通过设置自定义的Printer时,Printer会回调当前主线程执行的时间,通过对时间长的过滤认为是阻塞

源码分析

1、启动Sm的监控

Sm的监控通过SmCore去启动,读取xml的参数设置最小阻塞时间shortBlockThreshold和最大阻塞时间longBlockThreshold,通过mBlockCore.setBlockListener设置回调

  • 当满足短时间阻塞条件时,则回调onShortBlock()后并输出数据
  • 当满足长时间阻塞条件时,则回调onLongBlock()后并输出数据
public final class Sm extends ProduceableSubject<BlockInfo> implements Install<SmConfig> {
    private SmCore mBlockCore;
    private SmConfig mSmRealConfig;
    private SmConfig mSmConfig;
    private boolean mInstalled = false;

    @Override
    public synchronized boolean install(SmConfig config) {
        if (mInstalled) {
            L.d("Sm already installed, ignore.");
            return true;
        }
        this.mInstalled = true;
        this.mSmConfig = config;
        this.mSmRealConfig = wrapRealConfig(config);
        this.mBlockCore = new SmCore(GodEye.instance().getApplication(), this.mSmRealConfig.longBlockThreshold(), this.mSmRealConfig.shortBlockThreshold(), this.mSmRealConfig.dumpInterval());
        this.mBlockCore.setBlockListener(new BlockListener() {
            @Override
            public void onStart(Context context) {
            }

            @Override
            public void onStop(Context context) {
            }

            @WorkerThread
            @Override
            public void onShortBlock(Context context, ShortBlockInfo shortBlockInfo) {
                ThreadUtil.ensureWorkThread("Sm onShortBlock");
                produce(new BlockInfo(shortBlockInfo));
            }

            @WorkerThread
            @Override
            public void onLongBlock(Context context, LongBlockInfo blockInfo) {
                ThreadUtil.ensureWorkThread("Sm onLongBlock");
                produce(new BlockInfo(blockInfo));
            }
        });
        mBlockCore.install();
        L.d("Sm installed");
        return true;
    }
}

通过mBlockCore.install()将参数mMonitor设置成Printer

public void install() {
    ThreadUtil.createIfNotExistHandler(AbstractSampler.SM_DO_DUMP);
    Looper.getMainLooper().setMessageLogging(mMonitor);
}

2、采集Sm信息

通过SmCoreLooperMonitor监听Block回调

  • onEventStart:当Looper事件开始的时候,启动StackSampler采集堆栈
  • onEventEnd:当Looper事件结束的时候,结束StackSampler采集堆栈
  • onBlockEvent:当Looper事件结束的时候,如果当前的主线程发生阻塞则回调

onBlockEvent中属于长时间阻塞,则输出

  • 当前的Cpu信息CpuInfo
  • 当前线程的堆栈StackTraceElement
  • 当前内存信息MemoryInfo

onBlockEvent中属于短时间阻塞,则输出

  • 当前内存信息MemoryInfo
public final class SmCore {

    private Context mContext;
    private LooperMonitor mMonitor;
    private StackSampler stackSampler;
    private CpuSampler cpuSampler;

    private BlockListener mBlockListener;

    private long mLongBlockThresholdMillis;

    public SmCore(final Context context, long longBlockThresholdMillis, long shortBlockThresholdMillis, long dumpIntervalMillis) {
        this.mContext = context;
        this.mLongBlockThresholdMillis = longBlockThresholdMillis;
        this.stackSampler = new StackSampler(
                Looper.getMainLooper().getThread(), dumpIntervalMillis, getSampleDelay());
        this.cpuSampler = new CpuSampler(dumpIntervalMillis, getSampleDelay());
        this.mMonitor = new LooperMonitor(new LooperMonitor.BlockListener() {

            @Override
            public void onEventStart(long startTime) {
                startDump();
            }

            @Override
            public void onEventEnd(long endTime) {
                stopDump();
            }

            @Override
            public void onBlockEvent(final long blockTimeMillis, final long threadBlockTimeMillis, final boolean longBlock, final long eventStartTimeMilliis, final long eventEndTimeMillis, long longBlockThresholdMillis, long shortBlockThresholdMillis) {
                ThreadUtil.computationScheduler().scheduleDirect(() -> {
                    if (AndroidDebug.isDebugging()) {// if debugging, then ignore
                        return;
                    }
                    if (longBlock) {
                        //如果是长卡顿,那么需要记录很多信息
                        final boolean cpuBusy = cpuSampler.isCpuBusy(eventStartTimeMilliis, eventEndTimeMillis);
                        //这里短卡顿基本是dump不到数据的,因为dump延时一般都会比短卡顿时间久
                        final List<CpuInfo> cpuInfos = cpuSampler.getCpuRateInfo(eventStartTimeMilliis, eventEndTimeMillis);
                        final Map<Long, List<StackTraceElement>> threadStackEntries = stackSampler.getThreadStackEntries(eventStartTimeMilliis, eventEndTimeMillis);
                        final MemoryInfo memoryInfo = new MemoryInfo(MemoryUtil.getAppHeapInfo(), MemoryUtil.getAppPssInfo(mContext), MemoryUtil.getRamInfo(mContext));
                        LongBlockInfo blockBaseinfo = new LongBlockInfo(eventStartTimeMilliis, eventEndTimeMillis, threadBlockTimeMillis,
                                blockTimeMillis, cpuBusy, cpuInfos, threadStackEntries, memoryInfo);
                        if (mBlockListener != null) {
                            mBlockListener.onLongBlock(context, blockBaseinfo);
                        }
                    } else {
                        final MemoryInfo memoryInfo = new MemoryInfo(MemoryUtil.getAppHeapInfo(), MemoryUtil.getAppPssInfo(mContext), MemoryUtil.getRamInfo(mContext));
                        ShortBlockInfo shortBlockInfo = new ShortBlockInfo(eventStartTimeMilliis, eventEndTimeMillis, threadBlockTimeMillis,
                                blockTimeMillis, memoryInfo);
                        if (mBlockListener != null) {
                            mBlockListener.onShortBlock(context, shortBlockInfo);
                        }
                    }
                });
            }
        }, longBlockThresholdMillis, shortBlockThresholdMillis);
    }

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

    private void stopDump() {
        if (null != stackSampler) {
            stackSampler.stop();
        }
        if (null != cpuSampler) {
            cpuSampler.stop();
        }
    }
}

LooperMonitor继承Printer,当Looper开始执行函数和结束执行函数时,都会执行Printerprintln(String x)

public class LooperMonitor implements Printer {
    public static final String TAG = "LooperMonitor";
    // 长卡顿的阀值
    private long mLongBlockThresholdMillis;
    // 短卡顿的阀值
    private long mShortBlockThresholdMillis;
    // 一次事件开始时间
    private long mThisEventStartTime = 0;
    // 一次事件开始时间(线程内)
    private long mThisEventStartThreadTime = 0;
    private BlockListener mBlockListener = null;
    // 事件开始标记
    private boolean mEventStart = false;

    public interface BlockListener {
        void onEventStart(long startTime);

        void onEventEnd(long endTime);

        /**
         * 卡顿事件
         *
         * @param eventStartTimeMilliis     事件开始时间
         * @param eventEndTimeMillis        事件结束时间
         * @param blockTimeMillis           卡顿时间(事件处理时间)
         * @param threadBlockTimeMillis     事件真实消耗时间
         * @param longBlockThresholdMillis  长卡顿阀值标准
         * @param shortBlockThresholdMillis 短卡顿阀值标准
         */
        void onBlockEvent(long blockTimeMillis, long threadBlockTimeMillis, boolean longBlock,
                          long eventStartTimeMilliis, long eventEndTimeMillis, long longBlockThresholdMillis,
                          long shortBlockThresholdMillis);
    }

    LooperMonitor(BlockListener blockListener, long longBlockThresholdMillis, long shortBlockThresholdMillis) {
        if (blockListener == null) {
            throw new IllegalArgumentException("blockListener should not be null.");
        }
        mBlockListener = blockListener;
        mLongBlockThresholdMillis = longBlockThresholdMillis;
        mShortBlockThresholdMillis = shortBlockThresholdMillis;
    }

    /**
     * 更新阀值配置
     *
     * @param shortBlockThresholdMillis
     * @param longBlockThresholdMillis
     */
    public void setBlockThreshold(long shortBlockThresholdMillis, long longBlockThresholdMillis) {
        this.mShortBlockThresholdMillis = shortBlockThresholdMillis;
        this.mLongBlockThresholdMillis = longBlockThresholdMillis;
    }

    @Override
    public void println(String x) {
        if (!mEventStart) {// 事件开始
            mThisEventStartTime = System.currentTimeMillis();
            mThisEventStartThreadTime = SystemClock.currentThreadTimeMillis();
            mEventStart = true;
            mBlockListener.onEventStart(mThisEventStartTime);
        } else {// 事件结束
            final long thisEventEndTime = System.currentTimeMillis();
            final long thisEventThreadEndTime = SystemClock.currentThreadTimeMillis();
            mEventStart = false;

            long eventCostTime = thisEventEndTime - mThisEventStartTime;
            long eventCostThreadTime = thisEventThreadEndTime - mThisEventStartThreadTime;
            if (eventCostTime >= mLongBlockThresholdMillis) {
                mBlockListener.onBlockEvent(eventCostTime, eventCostThreadTime, true, mThisEventStartTime,
                        thisEventEndTime, mLongBlockThresholdMillis, mShortBlockThresholdMillis);
            } else if (eventCostTime >= mShortBlockThresholdMillis) {
                mBlockListener.onBlockEvent(eventCostTime, eventCostThreadTime, false, mThisEventStartTime,
                        thisEventEndTime, mLongBlockThresholdMillis, mShortBlockThresholdMillis);
            }
            mBlockListener.onEventEnd(thisEventEndTime);
        }
    }
}

由于源码中,println()会依次打印两次,在主线程函数执行开始调用一次,在主线程函数执行结束调用一次,利用这个规律

  • 通过mEventStart变量来控制当前是事件开始还是结束
  • 事件结束后,通过时间差计算出当前函数的时间耗时和当前线程的时间耗时
  • 如果超过规定的mLongBlockThresholdMillis时间,则触发长时间的阻塞行为
  • 如果超过规定的mShortBlockThresholdMillis时间,则触发短时间的阻塞行为
@Override
public void println(String x) {
    if (!mEventStart) {// 事件开始
        mThisEventStartTime = System.currentTimeMillis();
        mThisEventStartThreadTime = SystemClock.currentThreadTimeMillis();
        mEventStart = true;
        mBlockListener.onEventStart(mThisEventStartTime);
    } else {// 事件结束
        final long thisEventEndTime = System.currentTimeMillis();
        final long thisEventThreadEndTime = SystemClock.currentThreadTimeMillis();
        mEventStart = false;

        long eventCostTime = thisEventEndTime - mThisEventStartTime;
        long eventCostThreadTime = thisEventThreadEndTime - mThisEventStartThreadTime;
        if (eventCostTime >= mLongBlockThresholdMillis) {
            mBlockListener.onBlockEvent(eventCostTime, eventCostThreadTime, true, mThisEventStartTime,
                    thisEventEndTime, mLongBlockThresholdMillis, mShortBlockThresholdMillis);
        } else if (eventCostTime >= mShortBlockThresholdMillis) {
            mBlockListener.onBlockEvent(eventCostTime, eventCostThreadTime, false, mThisEventStartTime,
                    thisEventEndTime, mLongBlockThresholdMillis, mShortBlockThresholdMillis);
        }
        mBlockListener.onEventEnd(thisEventEndTime);
    }
}

在Sm采集过程中,涉及到堆栈的采集和CPU采集,它们的原理相似,通过定时采集当前的堆栈和CPU,以堆栈采集为例

  • 在采集过程中都会保存在集合里面,且超过了最大容量时,则会移除最后一个
  • 在采集过程中会记录每次采集的开始时间System.currentTimeMillis()
public class StackSampler extends AbstractSampler {
    ......
    
    @Override
    protected void doSample() {
        synchronized (sStackMap) {
            if (sStackMap.size() == mMaxEntryCount && mMaxEntryCount > 0) {
                sStackMap.remove(sStackMap.keySet().iterator().next());
            }
            sStackMap.put(System.currentTimeMillis(), mCurrentThread.getStackTrace());
        }
    }
}

这里需要注意的是堆栈的采集时间需要比发生阻塞的设置时长要短,如果主线程发生阻塞500ms的时候,那么采集堆栈如果设置为200ms的时候,就会在阻塞过程中产生2次采集,采集到的就是当前阻塞的堆栈,在获取堆栈的时候也会判断,当前采集堆栈的时间,是否在发生阻塞的时间区间内

Map<Long, List<StackTraceElement>> getThreadStackEntries(long startTime, long endTime) {
    Map<Long, List<StackTraceElement>> result = new LinkedHashMap<>();
    synchronized (sStackMap) {
        for (Long entryTime : sStackMap.keySet()) {
            //是否在发生阻塞的时间区间内
            if (startTime < entryTime && entryTime < endTime) {
                result.put(entryTime, Arrays.asList(sStackMap.get(entryTime)));
            }
        }
    }
    return result;
}

3、总结

Sm的采集原理用的是BlockCanary一样的原理,在此之外增加堆栈采集和CPU的采集,通过阻塞的时间区间作为标准获取对应的堆栈和CPU

本文地址:https://blog.csdn.net/qq_30379689/article/details/111881903