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"无埋点"统计技术预研—收集篇)