1、代码埋点
由开发人员在触发事件的具体方法里,植入多行代码把需要上传的参数上报至服务端。
2、可视化埋点
根据标识来识别每一个事件, 针对指定的事件进行取参埋点。而事件的标识与参数信息都写在配置表中,通过动态下发配置表来实现埋点统计。
3、无埋点
无埋点并不是不需要埋点,更准确的说应该是“全埋”, 前端的任意一个事件都被绑定一个标识,所有的事件都别记录下来。 通过定期上传记录文件,配合文件解析,解析出来我们想要的数据, 并生成可视化报告供专业人员分析 , 因此实现“无埋点”统计。
可视化埋点
首先,可视化埋点并非完全抛弃了代码埋点,而是在代码埋点的上层封装的一套逻辑来代替手工埋点,大体上架构如下图:
3104472-15d0364de7f22ecd.png
不过要实现可视化埋点也有很多问题需要解决,比如事件唯一标识的确定,业务参数的获取,有逻辑判断的埋点配置项信息等等。接下来我会重点围绕唯一标识以及业务参数获取这两个问题给出自己的一个解决方案。
唯一标识问题
唯一标识的组成方式主要是又 target + action 来确定, 即任何一个事件都存在一个target与action。 在此引入AOP编程,AOP(Aspect-Oriented-Programming)即面向切面编程的思想,基于 Runtime 的 Method Swizzling能力,来 hook 相应的方法,从而在hook方法中进行统一的埋点处理。例如所有的按钮被点击时,都会触发UIApplication的sendAction方法,我们hook这个方法,即可拦截所有按钮的点击事件。
3104472-3b1942be410c1e02.jpeg
这里主要分为两个部分 :
事件的锁定
事件的锁定主要是靠 “事件唯一标识符”来锁定,而事件的唯一标识是由我们写入配置表中的。
埋点数据的上报。
埋点数据的数据又分为两种类型: 固定数据与可变的业务数据, 而固定数据我们可以直接写到配置表中, 通过唯一标识来获取。而对于业务数据,我是这么理解的: 数据是有持有者的, 例如我们Controller的一个属性值, 又或者数据再Model的某一个层级。 这么的话我们就可以通过KVC的的方式来递归获取该属性的值来取到业务数据, 代码后面会有介绍。
整体代码示例
由于iOS中的事件场景是多样的, 在此我以UIControl, UITablview(collectionView与tableView基本相同), UITapGesture, UIViewController的PV统计 为例,介绍一下具体思路。
1、UIViewController PV统计
页面的统计较为简单,利用Method Swizzing hook 系统的viewDidLoad, 直接通过页面名称即可锁定页面的展示代码如下:
@implementation UIViewController (Analysis)
+(void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originalDidLoadSelector = @selector(viewDidLoad);
SEL swizzingDidLoadSelector = @selector(user_viewDidLoad);
[MethodSwizzingTool swizzingForClass:[self class] originalSel:originalDidLoadSelector swizzingSel:swizzingDidLoadSelector];
});
}
-(void)user_viewDidLoad
{
[self user_viewDidLoad];
//从配置表中取参数的过程 1 固定参数 2 业务参数(此处参数被target持有)
NSString * identifier = [NSString stringWithFormat:@"%@", [self class]];
NSDictionary * dic = [[[DataContainer dataInstance].data objectForKey:@"PAGEPV"] objectForKey:identifier];
if (dic) {
NSString * pageid = dic[@"userDefined"][@"pageid"];
NSString * pagename = dic[@"userDefined"][@"pagename"];
NSDictionary * pagePara = dic[@"pagePara"];
__block NSMutableDictionary * uploadDic = [NSMutableDictionary dictionaryWithCapacity:0];
[pagePara enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
id value = [CaptureTool captureVarforInstance:self withPara:obj];
if (value && key) {
[uploadDic setObject:value forKey:key];
}
}];
NSLog(@"\n 事件唯一标识为:%@ \n pageid === %@,\n pagename === %@,\n pagepara === %@ \n", [self class], pageid, pagename, uploadDic);
}
}
2、UIControl 点击统计
主要通过hook sendAction:to:forEvent: 来实现, 其唯一标识符我们用 targetname/selector/tag来标记,具体代码如下:
~~~
@implementation UIControl (Analysis)
+(void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originalSelector = @selector(sendAction:to:forEvent:);
SEL swizzingSelector = @selector(user_sendAction:to:forEvent:);
[MethodSwizzingTool swizzingForClass:[self class] originalSel:originalSelector swizzingSel:swizzingSelector];
});
}
-(void)user_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
[self user_sendAction:action to:target forEvent:event];
NSString * identifier = [NSString stringW