360 apm框架Argus源码解析(1)——开始

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/foolish0421/article/details/86616915

开始

先看一个简单的代码示例,演示如何集成360 Argus APM:

public class ArgusAPMApplication extends Application {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        boolean isUi = TextUtils.equals(getPackageName(), ProcessUtils.getCurrentProcessName());
        Config.ConfigBuilder builder = new Config.ConfigBuilder()
                .setAppContext(this)
                .setRuleRequest(new RuleSyncRequest())
                .setUpload(new CollectDataSyncUpload())
                .setAppName("apm_demo")
                .setAppVersion("1.0.0")
                .setApmid("apm_demo");//该ID是在APM的后台进行申请的

        //单进程应用可忽略builder.setDisabled相关配置。
        if (!isUi) { //除了“主进程”,其他进程不需要进行数据上报、清理等逻辑。“主进程”通常为常驻进行,如果无常驻进程,即为UI进程。
            builder.setDisabled(ApmTask.FLAG_DATA_CLEAN)
                    .setDisabled(ApmTask.FLAG_CLOUD_UPDATE)
                    .setDisabled(ApmTask.FLAG_DATA_UPLOAD)
                    .setDisabled(ApmTask.FLAG_COLLECT_ANR)
                    .setDisabled(ApmTask.FLAG_COLLECT_FILE_INFO);
        }
        //builder.setEnabled(ApmTask.FLAG_COLLECT_ACTIVITY_AOP); //activity采用aop方案时打开,默认关闭即可。
        builder.setEnabled(ApmTask.FLAG_LOCAL_DEBUG); //是否读取本地配置,默认关闭即可。
        Client.attach(builder.build());
        Client.isDebugOpen(false);//设置成true的时候将会打开悬浮窗
        Client.startWork();
    }

}

Config类对Argus进行一些基本的配置,其中IRuleRequest向云端请求规则,IUpload则对采集的数据进行上传。Client.startWork()会调用Manager.getInstance().startWork()Manager.getInstance().startWork()又通过调用TaskManager.getInstance().startWorkTasks()Manager.getInstance().startWork()的代码如下:

public void startWorkTasks() {
    if (taskMap == null) {
        LogX.d(Env.TAG, SUB_TAG, "taskMap is null ");
        return;
    }
    if (taskMap.get(ApmTask.TASK_ACTIVITY).isCanWork()) {
        // 云控为TaskConfig.ACTIVITY_TYPE_NONE,则本地开关优先
        int type = ArgusApmConfigManager.getInstance().getArgusApmConfigData().controlActivity;
        if (type == TaskConfig.ACTIVITY_TYPE_NONE) {
            if (Manager.getInstance().getConfig().isEnabled(ApmTask.FLAG_COLLECT_ACTIVITY_INSTRUMENTATION)) {
                LogX.o("activity local INSTRUMENTATION");
                InstrumentationHooker.doHook();
            } else {
                LogX.o("activity local aop");
            }
        } else if (type == TaskConfig.ACTIVITY_TYPE_INSTRUMENTATION) {
            LogX.o("activity cloud INSTRUMENTATION");
            InstrumentationHooker.doHook();
        } else {
            LogX.o("activity cloud type(" + type + ")");
        }

    }
    List<ITask> taskList = getAllTask();
    for (ITask task : taskList) {
        if (!task.isCanWork()) {
            continue;
        }
        if (DEBUG) {
            LogX.d(Env.TAG, SUB_TAG, "start task " + task.getTaskName());
        }
        task.start();
    }
}

taskMap是一个<String, ITask>类型的HashMap,通过TAskManager类中的registerTask方法将各种task(fps,activity等)添加到taskMap中。public void registerTask()Manager.getInstance().init()调用,可上溯到Client.attach(builder.build())。startWorkTasks方法先判断是Activity任务是否可以work,如果可以,就从配置中获取Activity的收集方式,如果云控策略是TaskConfig.ACTIVITY_TYPE_NONE,那么就以本地的开关优先:如果本地配置了ApmTask.FLAG_COLLECT_ACTIVITY_INSTRUMENTATION,就以instrumentation方式收集,否则,以aop方式收集Activity数据。startWorkTasks()方法最后的for循环,逐个检查taskMap中的task是否可以work,如果可以,就调用task的方法start,开启任务。每种task都有自己的task()实现,后文会详细讨论。
我们先看一下InstrumentationHooker类的doHook()方法:

public static void doHook() {
    try {
        hookInstrumentation();
        isHookSucceed = true;
    } catch (Exception e) {
        if (DEBUG) {
            LogX.e(TAG, "InstrumentationHooker", e.toString());
        }
    }
}

doHook()方法会调用本类的hookInstrumentation()方法,如果调用成功,就把属性isHookSucceed设置成true。hookInstrumentation()的代码如下:

private static void hookInstrumentation() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
    Class<?> c = Class.forName("android.app.ActivityThread");
    Method currentActivityThread = c.getDeclaredMethod("currentActivityThread");
    boolean acc = currentActivityThread.isAccessible();
    if (!acc) {
        currentActivityThread.setAccessible(true);
    }
    Object o = currentActivityThread.invoke(null);
    if (!acc) {
        currentActivityThread.setAccessible(acc);
    }
    Field f = c.getDeclaredField("mInstrumentation");
    acc = f.isAccessible();
    if (!acc) {
        f.setAccessible(true);
    }
    Instrumentation currentInstrumentation = (Instrumentation) f.get(o);
    Instrumentation ins = new ApmInstrumentation(currentInstrumentation);
    f.set(o, ins);
    if (!acc) {
        f.setAccessible(acc);
    }
}

hookInstrumentation()方法通过反射的方式获取当前ActivityThread的mInstrumentation属性的实例,返回给currentInstrumentation,并且用currentInstrumentation初始化一个ApmInstrumentation的实例ins,最后用ins的替换到当前ActivityThread中mInstrumentation的值,实现hook。ApmInstrumentation类的实现在后文详细分析。
Instrumentation方式是一种在运行期进行hook的方式。
Activity的另一种采集方式是AOP,在编译过程织入代码进行hook。后文详细分析。

猜你喜欢

转载自blog.csdn.net/foolish0421/article/details/86616915
360