iOS"无埋点"统计技术预研—收集篇

为了让公司领导和运营了解应用的某些方面的使用情况,如哪些功能点击量最高、APP版本分布情况、APP前后台使用时间等等,从而指导产品规划方向。如果不集成和依赖第三方库,就要探索如何实现自己的“无埋点”统计。本文属于技术预研篇。
1.埋点方案分类
埋点方案大体上可以归为三类:
(1)代码埋点,即在需要埋点的节点调用接口直接上传埋点数据,友盟、百度统计等第三方数据统计服务商大都采用这种方案;
(2)可视化埋点,即通过可视化工具配置采集节点,在前端自动解析配置并上报埋点数据,从而实现可视化“无痕埋点”, 代表方案Mixpanel;
(3)“无埋点”,它并不是真正的不需要埋点,而是前端自动采集全部事件并上报埋点数据,在后端数据计算时过滤出有用数据,代表方案GrowingIO。本文的部分思路也将借鉴这里。
2.收集数据分类
(1)通用全量事件收集

事件 描述
冷启动事件 App版本号、设备ID、渠道、内存使用情况等
前后台事件 APP进入前台或后台
页面事件 页面显示或隐藏
控件点击事件 控件的点击量
位置事件 上报用户所在地理位置
其他事件 其他
(2)业务相关数据按需收集
3.“无埋点”收集实现要求
(1)现有代码改动尽量少
(2)降低耦合性
(3)全量收集
4.“无埋点”收集实现方案
根据上面的实现要求,我们很容易想到以下方案:
(1)AOP(Aspect-Oriented-Programming)即面向切面编程的思想,就是动态的在函数调用的前后插入数据收集的代码。将日志记录、性能统计、安全控制、异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,将它们独立到非业务逻辑的方法中,进而改 变这些行为的时候不影响业务逻辑的代码
(2)在 Objective-C 中的实现是基于 Runtime 特性的 Method Swizzling 来 hook 相应的方法
(3)开源框架:Aspects框架
5.Aspect原理
方法交换常用代码:
+ (void)load{static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{SEL origilaSEL = @selector(addTarget: action: forControlEvents:); SEL hook_SEL = @selector(xw_addTarget: action: forControlEvents:); //交换方法 Method origilalMethod = class_getInstanceMethod(self, origilaSEL); Method hook_method = class_getInstanceMethod(self, hook_SEL); class_addMethod(self, origilaSEL, class_getMethodImplementation(self, origilaSEL), method_getTypeEncoding(origilalMethod)); class_addMethod(self, hook_SEL, class_getMethodImplementation(self, hook_SEL), method_getTypeEncoding(hook_method)); method_exchangeImplementations(class_getInstanceMethod(self, origilaSEL), class_getInstanceMethod(self, hook_SEL)); });

Aspects是基于method swizzle的第三方库,用的就是AOP面向切面编程思想。Aspects要的是实现一个通用的IMP,任意方法任意参数都可通过这个IMP中转。
库的核心代码有两个方法:
//aspect_hookSelector:表示要拦截指定对象的方法。 //withOptions:是一个枚举类型,AspectPositionAfter/AspectPositionInstead/AspectPositionBefore 。 //usingBlock:就是拦截事件后执行的自定义方法。我们可以在这个block里面添加我们要执行的代码 + (id)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error; /// Adds a block of code before/instead/after the current `selector` for a specific instance. - (id)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error;

Aspect源码主要用到了消息转发机制,有时间的可以研究下源码。下图展示的是消息转发机制的流程图:
iOS"无埋点"统计技术预研—收集篇
文章图片
image.png 6.事件拦截示例
(1)点击事件拦截
事件 hook的系统类 hook的系统方法
按钮的点击 UIApplication sendAction:to: from:forEvent:
手势操作 UIGestureRecognizer initWithTarget:action: addTarget:action:
列表点击 UITableView和UICollectionView setDelegate:、tableView:didSelectRowAtIndexPath:、collectionView:didSelectItemAtIndexPath:等
系统弹窗 UIAlertView setDelegate:、alertView:clickedButtonAtIndex:
系统导航栏返回按钮 UINavigationController navigationBar:didPopItem:
(2)页面事件拦截
事件 hook的系统类 hook的系统方法
页面事件 UIVIewController viewDidLoad 、viewWillAppear: 、viewDidAppear: 、viewWillDisappear:等生命周期方法:
iOS"无埋点"统计技术预研—收集篇
文章图片
代码示例.png 7.viewPath
为了对 某个页面的某个 view 进行数据收集,view需要一个唯一标识。视图结构可以看成是一颗树(viewTree)。
规定:在 viewTree 中,由一个 view 到根节点之间的每个节点的名称与深度(index)共同组成的信息,构成了此 view 的viewPath。
iOS"无埋点"统计技术预研—收集篇
文章图片
viewPath示例.png
iOS"无埋点"统计技术预研—收集篇
文章图片
growingIO xpath.png
参考文章: https://www.jianshu.com/p/69ce01e15042 【iOS"无埋点"统计技术预研—收集篇】

    推荐阅读