作者 | 金小俊,上汽集团享道出行,资深移动开发工程师
在发展日新月异的移动互联网时代,数据扮演着极其重要的角色。埋点作为一种最简单最直接的用户行为统计方式,能够全面精确的采集用户的使用习惯以及各功能点的迭代反馈等等,有了这些数据才能更好的驱动产品的决策设计和新业务场景的规划。本文旨在提出一种轻量级非侵入式的埋点方案,其主要有以下三方面优势
• 支持动态下发埋点配置
• 物理隔离埋点代码和业务代码
• 插件式的埋点功能实现
该方案通过维护一个JSON文件来指定埋点所在的类和方法,继而利用AOP的方式在对应的类和方法执行时动态嵌入埋点代码。对于需要逻辑判断来确定埋点值的场景,提供hook方法的入参,以及所在类的属性值读取,根据相应的状态值设置不同的埋点
埋点配置
埋点配置JSON表中包含需要hook的类名class和具体的事件event信息,event中包括hook的方法和对应的埋点值。如下所示
{
"version": "0.1.0",
"tracking": [
{
"class": "RJMainViewController",
"event": {
"rj_main_tracking": ["tripTypeViewChangedWithIndex:","tripLabClickWithLabKey:"
],
"user_fp_slide_click": "clickNavLeftBtn",
"user_fp_reflocate_click": "clickLocationBtn"}},
{
"class": "RJTripHistoryViewModel",
"event": {
"user_mytrip_show": "tableView:didSelectRowAtIndexPath:"}},
{
"class": "RJTripViewController",
"event": {
"rj_trip_tracking": "callServiceEvent"}}
]}
简单来说就是本来埋点需要手动在该方法写入埋点代码来记录埋点值,现在通过AOP的方式物理隔离埋点代码和业务代码,避免埋点的逻辑侵入污染业务逻辑。埋点包括固定埋点和需要逻辑判断的场景化埋点,固定埋点如下所示
{
"class": "RJTripHistoryViewModel",
"event": {
"user_mytrip_show": "tableView:didSelectRowAtIndexPath:"}}
RJTripHistoryViewModel为类名,tableView:didSelectRowAtIndexPath:为需要hook的该类中的方法,而user_mytrip_show则是具体的埋点值,也就是当RJTripHistoryViewModel中的tableView:didSelectRowAtIndexPath:方法执行的时候记录埋点值user_mytrip_show
{
"class": "RJTripViewController",
"event": {
"rj_trip_tracking": "callServiceEvent"
}
},
对于场景化埋点,则需要提供一个impl类来提供相应的逻辑判断。比如上述配置表中的rj_trip_tracking为场景埋点的实现类,在该类中根据状态量返回对应的埋点值,即当callServiceEvent方法执行时会去找rj_trip_tracking这个埋点impl同名类,取该类返回的埋点值记录埋点。需要注意到是event中的key值既可以作为埋点值也可以作为impl的类名,埋点库会首先判断是否存在对应的类,存在即认为是impl实现类,从该类中取具体的埋点值。反之,则认为是固定埋点值
配置表中的类名和方法名需要对应,在hook的时候会去匹配,如果发现类中不存在对应的方法,则会自动触发断言
固定埋点
对于固定的埋点,只需要在对应的方法执行时直接记录埋点,利用Aspects来hook指定的类和方法,代码如下所示
[class aspect_ho