Matrix源码分析(七)之 StartupTracer 工作原理

关于启动方面也是大家都非常关注的地方,但是如何做到无侵入的监听的App的启动状态呢,这个问题就比较麻烦了,这里先分析一下Matrix的方案,

冷启动

1:使用反射代理 ActivityThread 中管理 四大组件生命周期事件的 mH 这个Handler 中的 mCallback

在App的启动过程中,是需要先经过Application 的初始化的过程的,在执行完这个Application初始化之后,才会通过AMS 再次调用 启动 Activity ,也就是说 Application 与 Activity 的创建是执行的不同的Message,那么是不是只要判断出来第一个被启动的 Service 或者 Activity 再或者 BroadCastReceiver 的Message 就证明Application 就已经初始化成功了,明明是四大组件为什么 Matrix 会区别对待 ContentProvider 呢,看下面图片

在 ActivityThread 中的 handleBindApplication 内,在调用 Application 初始化onCreate之前,就已经启动了ContentProvider,正常情况下写了也是没有作用的,

到了这里我们就知道 Matrix 如何收集 Application onCreate 与 attachBaseContext 这两个方法的耗时了,非常关键性的一步

2:如何区分是冷启动还是温启动呢

首先需要先明确一下冷启动与温启东的区别,冷启动会创建进程,进程内的变量应该都是默认值,温启动是进程还在,进程内的变量还被保留,只是Activity 处于销毁或者不可见的状态,

那我用一个long 类型的变量flag 来标识是否是冷启动,默认这个值给0, 并且使用 ActivityLifecycleCallbacks 来监听 Activity 生命周期,当执行到 onCreate时 ,如果现存的 activity 个数是 0,并且 这个 flag 是0代表的什么,没错就是冷启动,如果 现存的 activity 个数是0 ,但是 flag 不为初始值就代表着温启动 ,

3:如何获取 Activity onFocusedChange 方法的调用时机

在ASM字节码插装的过程中,向所有Activity的onFocusChange方法内插入了一个方法,当执行时就会回调 AppMethodBeat 中持有的回调列表

那么在 Activity 可见也就是Activity 的 onFocusChange =true 的情况下,就能判断出来从开始到结束的整个时长,同时配合着 AppMethodBeat 在app实际启动时间大于阈值时,就可以收集所有在启动过程中所有方法的耗时,

到了这里基本的流程基本就已经完成了,接下来我们再来分析一下代码

先来看他的初始化代码

ini 复制代码
public StartupTracer(TraceConfig config) {
    this.config = config;
    this.isStartupEnable = config.isStartupEnable();
    this.splashActivities = config.getSplashActivities();
    this.coldStartupThresholdMs = (long)config.getColdStartupThresholdMs();
    this.warmStartupThresholdMs = (long)config.getWarmStartupThresholdMs();
    this.isHasActivity = config.isHasActivity();
    ActivityThreadHacker.addListener(this);
}

这里会向我们前面介绍的 ActivityThreadHacker 内添加一个 onApplicationCreateEnd 回调,

再来看一下他的alive 方法

typescript 复制代码
protected void onAlive() {
    super.onAlive();
    MatrixLog.i("Matrix.StartupTracer", "[onAlive] isStartupEnable:%s", new Object[]{this.isStartupEnable});
    if (this.isStartupEnable) {
        // 监听 onFocusChange状态的监听
        AppMethodBeat.getInstance().addListener(this);
        // 监听Activity的生命周期状态
        Matrix.with().getApplication().registerActivityLifecycleCallbacks(this);
    }
}

在这里他就是添加了两个监听,先去看 Activity 生命周期状态的监听

kotlin 复制代码
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    MatrixLog.i("Matrix.StartupTracer", "activeActivityCount:%d, coldCost:%d", new Object[]{this.activeActivityCount, this.coldCost});
    // 如果 Activity 个数是 0 ,并且 这个 coldCost >0 ,证明进程是还在的,他就是温启动
    if (this.activeActivityCount == 0 && this.coldCost > 0L) {
        this.lastCreateActivity = SystemClock.uptimeMillis();
        MatrixLog.i("Matrix.StartupTracer", "lastCreateActivity:%d, activity:%s", new Object[]{this.lastCreateActivity, activity.getClass().getName()});
        this.isWarmStartUp = true;
    }

    ++this.activeActivityCount;
    if (this.isShouldRecordCreateTime) {
        this.createdTimeMap.put(activity.getClass().getName() + "@" + activity.hashCode(), SystemClock.uptimeMillis());
    }

}

这里就做了一个简单的判断,将温启动 与 (冷启动 + 启动Activity)区分开了,

我们再来看看他在 onActivityFocused 获取到焦点后是如何将普通的 启动Activity 与 冷启动也区分开的

kotlin 复制代码
public void onActivityFocused(Activity activity) {
    // 如果 ActivityThreadHacker.sApplicationCreateScene 这里面是默认值,那么就证明 Hook失败了,
    // 那么就不能进行数据分析了
    if (ActivityThreadHacker.sApplicationCreateScene == -2147483648) {
        
    } else {
        String activityName = activity.getClass().getName();
        // 重点 判断是否是冷启动
        if (this.isColdStartup()) {
            boolean isCreatedByLaunchActivity = ActivityThreadHacker.isCreatedByLaunchActivity();
            
            String key = activityName + "@" + activity.hashCode();
            Long createdTime = (Long)this.createdTimeMap.get(key);
            if (createdTime == null) {
                createdTime = 0L;
            }

            this.createdTimeMap.put(key, SystemClock.uptimeMillis() - createdTime);
            if (this.firstScreenCost == 0L) {
                this.firstScreenCost = SystemClock.uptimeMillis() - ActivityThreadHacker.getEggBrokenTime();
            }

            if (this.hasShowSplashActivity) {
                this.coldCost = SystemClock.uptimeMillis() - ActivityThreadHacker.getEggBrokenTime();
            } else if (this.splashActivities.contains(activityName)) {
                this.hasShowSplashActivity = true;
            } else if (this.splashActivities.isEmpty()) {
                if (isCreatedByLaunchActivity) {
                    this.coldCost = this.firstScreenCost;
                } else {
                    this.firstScreenCost = 0L;
                    this.coldCost = ActivityThreadHacker.getApplicationCost();
                }
            } else if (isCreatedByLaunchActivity) {
                this.coldCost = this.firstScreenCost;
            } else {
                this.firstScreenCost = 0L;
                this.coldCost = ActivityThreadHacker.getApplicationCost();
            }

            if (this.coldCost > 0L) {
                Long betweenCost = (Long)this.createdTimeMap.get(key);
                if (null != betweenCost && betweenCost >= 30000L) {
                    MatrixLog.e("Matrix.StartupTracer", "%s cost too much time[%s] between activity create and onActivityFocused, just throw it.(createTime:%s) ", new Object[]{key, SystemClock.uptimeMillis() - createdTime, createdTime});
                    return;
                }

                this.analyse(ActivityThreadHacker.getApplicationCost(), this.firstScreenCost, this.coldCost, false);
            }
            // 重点是否是温启动
        } else if (this.isWarmStartUp()) {
            this.isWarmStartUp = false;
            long warmCost = SystemClock.uptimeMillis() - this.lastCreateActivity;
            MatrixLog.i("Matrix.StartupTracer", "#WarmStartup# activity:%s, warmCost:%d, now:%d, lastCreateActivity:%d", new Object[]{activityName, warmCost, SystemClock.uptimeMillis(), this.lastCreateActivity});
            if (warmCost > 0L) {
                this.analyse(0L, 0L, warmCost, true);
            }
        }

    }
}

他这里使用了一个 isColdStartup 方法来将冷启动 与 普通启动区分开来 ,我们来看看 他是如何判断的

typescript 复制代码
private boolean isColdStartup() {
    return this.coldCost == 0L;
}

非常简单的明了,只要 coldCost!=0 ,就证明他不是冷启动,那也就证明进入冷启动后这个 coldCost 肯定会被赋值, 剩下的冷启动的耗时分析就是各种计算了,

到了这里启动耗时分析就完成了

相关推荐
叶羽西1 小时前
Android15系统中(娱乐框架和车机框架)中对摄像头的朝向是怎么定义的
android
Java小白中的菜鸟1 小时前
安卓studio链接夜神模拟器的一些问题
android
莫比乌斯环1 小时前
【Android技能点】深入解析 Android 中 Handler、Looper 和 Message 的关系及全局监听方案
android·消息队列
编程之路从0到11 小时前
React Native新架构之Android端初始化源码分析
android·react native·源码阅读
行稳方能走远1 小时前
Android java 学习笔记2
android·java
编程之路从0到12 小时前
React Native 之Android端 Bolts库
android·前端·react native
爬山算法2 小时前
Hibernate(38)如何在Hibernate中配置乐观锁?
android·java·hibernate
行稳方能走远2 小时前
Android java 学习笔记 1
android·java
zhimingwen2 小时前
【開發筆記】修復 macOS 上 JADX 啟動崩潰並實現快速啟動
android·macos·反編譯