埋点方案对比
1、手动在onClick等方法下粗鲁的➕埋点,嗯,是最最原始的一种方式了,没有比这个代码更加简洁的了,按需来加,缺点是,麻烦,得一个个加,漏掉没加,就只能等下一次了。
2、AOP方式
嗯,AOP是什么鬼,用简单的话来将,他就是面向切面编程的意思,可是面向切面编程又是什么鬼,在具体点就是,你能够将方法看成一个面,方法执行之前,执行过程中,执行之后就是一个一个的点,所谓的切点,就是这么来的。
因此,自动埋点就对于aop来说就小菜一碟啦,不仅如此,因为可以在方法前后打桩,所以,对方法做耗时统计也是小菜一碟,AOP通常需要通过配置注解来实现
AOP方式的特点是,埋点简单,一个注解就可以搞定,缺点还是不能自动,还是要你写代码,一个注解别看他简单,这也是代码啊。
3、asm的方式。
asm方式在粗的方面和aop方式对比的话,有点像,但是可以说更加底层,他能够做道对.class文件的修改,什么意思,就是说,分析.class文件,找到那些需要埋点的方法,比如,onclick,onlongclick等等,让后进行黑科技,一顿操作,加点字节码进去,重新编一个.class文件,替换之前的文件。
这种方式的特点就是,实现起来比较复杂,没那么容易玩的,好处就是,可以实现埋点自动化。
AndroidStudio上痛殴asm方式的原理
首先寄出这个apk打包的过程,图中,沃恩可以看到,.classfile会被打包成dex文件,你可以别以为dex文件就一个,可能有多个。
Google官方在Android Gradle的1.5.0 版本以后提供了 Transfrom API,这样一个接口给我们提供了一个可能,添加一个Transfrom的话,可以让我们对.class文件搞事了。
那么,我们需要关注的问题点有哪些:
1、需要对哪些.class文件进行处理,不可能都处理吧,肯定有些特点,怎么把这些文件拧出来。
2、拿到.class之后,怎么找到需要搞事的方法。
3、如和对搞事的方法搞事。
要实现上面这些东西,比较明朗的方式是,我们需要定义一个Gradle插件,下面就来给出解决以上三个问题的思路,首先,我们定义一个Transform,继承自com.android.build.api.transform.Transform
红框部分,解决了我们遇到的第一个问题,打包到dex中的文件其实有两种不同的组织方式,一种是jar,实际上也是一个.class的压缩包,其次是我们项目的,以文件夹的形式组织起来。
第二个问题,我们如何柠出我们需要处理的.class,这里其实需要我们定义一些规则,比如按照包名,来决定取舍,按照有无某内注解来决定是否需要对.class处理。
你比如:
代码语言:javascript复制if (className.contains('R$') ||
className.contains('R2$') ||
className.endsWith('R') ||
className.endsWith('R2') ||
className.endsWith('BuildConfig')) {
return false
}
为了更加好用,你可以做成一种可以配置的方式。
第三个问题,.class找到了,要处理,我们如何来搞事,想一想,我们是要对.class字节码来修改的,这种操作我们可不是太擅长,那么,有没有那些工具可以呢,当然是有的,这个工具就是我们今天的主题ASM。
ASM框架中的核心类有以下几个:
- ClassReader:该类用来解析编译过的class字节码文件。
- ClassWriter:该类用来重新构建编译后的类,比如说修改类名、属性以及方法,甚至可以生成新的类的字节码文件。
- ClassVisitor:主要负责 “拜访” 类成员信息。其中包括标记在类上的注解,类的构造方法,类的字段,类的方法,静态代码块。
- AdviceAdapter:实现了MethodVisitor接口,主要负责 “拜访” 方法的信息,用来进行具体的方法字节码操作。
关于ASM框架更加具体的介绍请狂点这里; 我们首先来卡看ClassVisitor:
他暴露了给我们范文方法的接口,好,找到方法了,怎么在里面搞事呢?
答案是使用MethodVisitor,通过asm提供的这个接口,我们就可以把自己构造的字节码插入到需要打桩的方法中,基本操作大概是:
代码语言:javascript复制methodVisitor.visitMethodInsn(opcode, owner, methodName, methodDesc, false)
一个比较直观感受的对方法加代码的demo
工具的实现demo
顺着这个思路,笔者在luffy的基础上封装了一个更加容易体验的自动化埋点方式,感谢@JieYuShi
工具地址:Android_auto_track
工具包括两部分:插件,和库,插件实现基初功能,库的话,是将一些功能和接口暴露出来,想自己玩一把的话可以直接clone源码,体验。
参考资料
https://www.ibm.com/developerworks/cn/java/j-lo-asm30/index.html