一个获取Android应用(或Activity)启动时间的工具

需求: 要求得到某个Activity的启动时间

实现:

1. adb shell下面存在am命令, 使用start -W -n参数, 即可获取Activity启动时间的信息;

2. 上述Activity的启动信息中, 包含了本次启动时间.

================================= 需求变更的分割线 =================================

需求: 要求得到某个Activity的启动时间, 需要将此功能集成到APK

实现:

扫描二维码关注公众号,回复: 502644 查看本文章

1. 通过Process process = Runtime.getRuntime().exec(cmd)执行命令;

2. 通过上述process.getInputStream()获取命令执行结果;

3. 解析命令执行结果, 得到Activity启动时间.

但是, 上面这个方案看着不是很爽, 想找个替代方案.

百度了一下, 豁然开朗: am命令是用java实现的, cat一下system/bin/am, 就可以看到它里面实际上封装了system/framework/am.jar. 然后在源码中找到了am.jar的工程(此处存在一堆找代码的过程, 最后再扯).

来读一下代码:

1. 既然是java工程, 那势必要先找到public static void main();

2. 发现里面就一句话:

(new Am()).run(args);
3. 我X, 过分, 这个run()方法还是在父类BaseCommand里的. 没错, 这一步我就是写来吐槽找代码麻烦的!

4. 父类的run()方法的关键代码:

public void run(String[] args) {
    if (args.length < 1) {
        onShowUsage(System.out);
        return;
    }
    ...
    onRun();
    ...
}
即校验完参数个数, 就开始入正题了, 正题跑的是Am.onRun();

5. 接下来就好办了, 找start参数的处理逻辑: 由于我关心的是最后输出内容中的"ThisTime", 因此找到它是怎样赋值, 就算是定位到数据源. 于是有了方案如下:

实现二:

1. Am.java中, 通过IActivityManager.startActivityAndWait()方法, 启动目标Activity, 并从方法返回值中获取Activity启动时间等信息. 由于IActivityManager及其方法是hide的, 所以需要通过反射获取;

2. 获取IActivityManager实例:

Class activityManagerNativeClazz = Class.forName("android.app.ActivityManagerNative");
Method getIActivityManager = activityManagerNativeClazz.getDeclaredMethod("getDefault", new Class[] {});
Object iActivityManagerInstance = getIActivityManager.invoke(activityManagerNativeClazz, null);
2. 从IActivityManager中找到startActivityAndWait()这个方法(没有通过方法 + 参数类型来拿, 是因为懒, 这个方法有好多参数... 并且同名的方法就这一个, 不怕拿错):
Method startActivityAndWait = null;
for (Method method : iActivityManagerInstance.getClass().getDeclaredMethods()) {
    if ("startActivityAndWait".equals(method.getName())) {
        startActivityAndWait = method;
        break;
    }
}

3. 构造启动Activity的Intent. Am.java中, 通过目标包名 + 目标Activity类名构造Intent, 可以照搬为如下代码:

Intent intent = new Intent();
intent.setComponent(ComponentName.unflattenFromString(packageName + "/" + activityName));
4. invoke方法, 启动目标Activity:
startActivityAndWait.invoke(iActivityManagerInstance, params);
5. 上一步执行的返回值是android.app.IActivityManager$WaitResult类型的, 由于它也属于hide API, 所以获取到实例后, 再次通过反射拿到WaitResult.thisTime的值, 作为Activity启动时间.

================================= 与方案无关的分割线 =================================

补上找代码的过程:

1. 如果am.jar不是第三方的库(应该不会是, 因为这个jar包处理的是Android的东西), 那么就应该由系统中的java文件编译而来. 那么就一定会有编译中间件. 经查找中间件, 发现其中包含有am_intermediates, 那么可以确定一个信息, am.jar的module name就是am;

2. 通过对mk文件进行检索, 关键字: LOCAL_MODULE := am;

3. 定位到工程源码位于frameworks/base/cmds/am;

4. 这是个java工程, 并且整个工程只有一个源码文件...

5. Am.java的父类BaseCommand, 通过文件名检索, 可以找到位于frameworks/base/core/java/com/android/internal/os路径下.

猜你喜欢

转载自oliveexcel.iteye.com/blog/2191396
今日推荐