Android全埋点技术白皮书_44页_9mb
报告摘要
Android 全埋点技术白皮书总结
全埋点概述
全埋点是一种无码埋点技术,用于自动收集用户的所有行为数据。它主要采集四种事件:
- $AppStart:表示App启动,包括冷启动和热启动。
- $AppEnd:表示App退出,包括正常退出、进入后台、App崩溃、App被强杀。
- $AppViewScreen:表示App页面浏览,即Activity切换。
- $AppClick:表示App控件被点击。
其中,$AppClick的采集难度最大,是全埋点的核心。
全埋点的实现方式主要有两种:静态代理和动态代理。静态代理通过Gradle插件在编译期间插入或修改代码,如AspectJ、ASM、Javassist等;动态代理则在运行时进行拦截,如代理View.OnClickListener、WindowCallback、View.AccessibilityDelegate等。
$AppViewScreen 全埋点
原理概述
通过注册Application.ActivityLifecycleCallbacks接口,SDK可以在Activity的onResume生命周期中触发页面浏览事件($AppViewScreen)。系统会先回调ActivityLifecycleCallbacks的onActivityResumed方法,再执行Activity的onResume方法。
实现步骤
- 在Application的
onCreate()方法中初始化埋点SDK。 - SDK通过
registerActivityLifecycleCallback注册ActivityLifecycleCallbacks。 - 在
onActivityResumed中获取当前Activity,并触发$AppViewScreen事件。
缺点
- 需要API 14+支持。
知识点
Application.ActivityLifecycleCallbacks- Activity生命周期
onActivityResumed方法
$AppStart、$AppEnd 全埋点
原理概述
通过监听Activity的生命周期,结合ContentProvider和SharedPreferences实现跨进程的数据共享,判断App是否处于前台或后台。当页面退出且30秒内没有新的页面打开时,触发$AppEnd事件;当页面启动且与上一页面的退出时间间隔超过30秒时,触发$AppStart事件。
实现步骤
- 注册
ActivityLifecycleCallbacks以监听Activity生命周期。 - 使用ContentProvider和SharedPreferences实现跨进程数据通信。
- 在
onPause中启动定时器,onStart中判断是否需要触发$AppEnd事件。
缺点
- 无法处理多进程和App崩溃/强杀的情况。
知识点
ContentProviderSharedPreferencesContentObserverActivityLifecycleCallbacks
$AppClick 全埋点
一、代理 View.OnClickListener
- 通过遍历RootView,代理其OnClickListener,插入埋点代码。
- 需要使用
android.R.id.content获取RootView,但该方式无法采集MenuItem点击事件。 - 解决方案:使用
DecorView来获取完整的视图树,包括MenuItem。
二、代理 WindowCallback
- 通过代理WindowCallback的
dispatchTouchEvent方法,获取被点击的View对象。 - 需要API 14+支持
ActivityLifecycleCallbacks,API 15+支持View.hasOnClickListener()。
三、代理 View.AccessibilityDelegate
- 通过代理
sendAccessibilityEvent方法,插入埋点代码。 - 适用于无法通过OnClickListener代理的控件,如MenuItem。
- 需要API 14+支持
ActivityLifecycleCallbacks,API 15+支持View.hasOnClickListener()。
四、AspectJ
- 使用AOP思想,在编译期将埋点代码插入目标方法。
- 支持编译期和类加载期织入,不支持Lambda语法。
- 需要自定义Gradle插件,并通过Transform API操作.class文件。
五、ASM
- 通过字节码操作框架,在编译后的.class文件中插入埋点代码。
- 使用ClassReader、ClassWriter、ClassVisitor等类进行字节码操作。
- 优点是灵活性高,可精确控制字节码修改。
六、Javassist
- 一个字节码操作库,允许在运行时直接修改已编译的类。
- 提供
insertBefore、insertAfter等方法在方法体中插入代码。 - 可以动态修改类定义,但需要注意内存管理和类冻结问题。
七、AST
- AST(Abstract Syntax Tree)是一种代码结构表示方式,用于在编译阶段分析和修改代码。
- 通常用于代码注入,如在编译时插入埋点逻辑。
全埋点方案对比
| 方案 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| View.OnClickListener | 动态代理 | 实现简单,兼容性较好 | 无法采集动态创建的View、MenuItem等 |
| WindowCallback | 动态代理 | 可以采集更多View类型 | 效率较低,对性能影响大 |
| View.AccessibilityDelegate | 动态代理 | 可以采集MenuItem等控件 | 效率较低,对性能影响大 |
| AspectJ | 静态代理 | 无侵入性,可统一维护 | 无法兼容Lambda语法,需自定义Gradle插件 |
| ASM | 静态代理 | 灵活性高,可精确控制字节码修改 | 需要较深的字节码知识 |
| Javassist | 静态代理 | 操作简单,无需深入字节码知识 | 有内存溢出风险,不支持Lambda语法 |
| AST | 静态代理 | 支持代码注入 | 实现复杂,需依赖编译器支持 |
关键技术点
- Application.ActivityLifecycleCallbacks:用于监听Activity生命周期,是全埋点的基础。
- DecorView:包含完整的视图树,用于采集MenuItem等控件的点击事件。
- ViewTreeObserver.OnGlobalLayoutListener:用于监听视图布局变化,采集动态创建的View。
- ContentProvider + SharedPreferences:用于跨进程数据共享,实现App状态判断。
- ContentObserver:用于监听ContentProvider的数据变化。
- 字节码操作:ASM和Javassist等工具用于在编译阶段插入埋点逻辑。
- Gradle Transform API:用于在打包过程中操作.class文件,实现静态代理埋点。
- 反射:用于获取和修改View的属性和方法,但效率较低。
总结
全埋点技术的核心在于如何自动采集用户点击行为,其难点在于兼容性和性能。不同方案各有优劣,选择时应综合考虑效率、兼容性、扩展性等。静态代理方案(如AspectJ、ASM、Javassist)在编译阶段插入代码,对运行时性能影响较小,但实现复杂;动态代理方案(如代理OnClickListener、WindowCallback、AccessibilityDelegate)实现简单,但可能影响性能,并且无法采集所有控件的点击事件。随着Android生态的发展,兼容性问题变得愈发重要,选择合适的埋点方案需要结合具体项目需求。
试读结束,高清完整版pdf/doc/ppt,请点下载