一、埋点的作用:
- 应用趋势分析:清晰展现应用的新增用户、活跃用户、启动次数、版本分布、行业指标等数据,方便从整体掌控应用的运营情况及增长动态。
- 渠道分析:在哪里推广最有效?从哪里获取的用户最有价值?通过数据对比评估不同渠道的用户质量和活跃程度,从而衡量推广效果。
- 留存分析:可以掌握每日(周/月)的新增用户在初次使用后一段时间内的留存率,留存率的高低一定程度上反映了产品和用户质量的好坏。
- 用户属性:用户的基本属性和行为特征,全面了解用户。
- 行为分析:针对性地进行应用内的数据统计,了解用户的产品使用细节及行为特征,帮助团队寻找产品改进的突破点,评估产品优化的效果。
二、自动化埋点SDK的研发背景
1、代码埋点
优点:灵活性高,能满足大量个性化需求。
缺点:开发者需要手动在需要埋点的节点植入埋点代码,可能埋点代码也需要植入一定的业务逻辑。
代码耦合严重,复用性差,工作量大,难以维护。
2、自动化埋点
优点:
- 可以较大程度降低开发成本,不受版本更新影响 。
- 解耦业务代码,易维护,可移植性强。
- 解决了数据回溯问题,可查看历史数据。
- 避免了使用三方SDK可能造成用户关键数据丢失及企业泄密等问题。
缺点:未解决个性化自定义获取数据的问题,缺乏数据获取的灵活性。
三、数据采集
数据采集原理:利用object-c的runtime机制,对有需要的类和事件进行方法交换,进行事件拦截,注入埋点代码,实现数据统计的功能,具体做法是:
重载类的+(void)load
方法,在程序加载到内存时利用runtime的method_exchangeImplementations
等接口,
将方法(设为M)的实现互相交换,当方法M被调用时就会被Hook,执行我们的方法。
1
2
3
4
5
6
7
8
9
10
11
|
/// ClickKit 类
+ (void)swapMethod:(Class)class origMethod:(SEL)origSelector newMethod:(SEL)newSelector{
Method originalMethod = class_getInstanceMethod(class, origSelector);
Method swizzledMethod = class_getInstanceMethod(class, newSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
}
+ (BOOL)addMethod:(Class)class sel:(SEL)sel method:(IMP)method
{
return class_addMethod(class, sel, method, "v@:@@");
}
|
(1)页面统计(PV)
PV统计原理:通过hook UIViewController的以下函数,达到采集类名等功能:
特别注意:必须实现以下三个函数的super调用,否则,页面自动化埋点无法触发
1
2
3
|
- (void)viewDidLoad;
- (void)viewDidAppear:(BOOL)animated;
- (void)viewDidDisappear:(BOOL)animated;
|
(2)事件统计(event)
事件id:通过对点击事件进行方法拦截,获取当前点击控件及控件所属target,根据点击视图的响应者链,逐级取到控件的所属view tree,建议点击视图增加tag。
针对单一点击事件,以UIControl为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@implementation UIControl (ClickKit)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[ClickKit swapMethod:[self class]
origMethod:@selector(sendAction:to:forEvent:)
newMethod:@selector(swizzle_sendAction:to:forEvent:)];
});
}
- (void)swizzle_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
[self swizzle_sendAction:action to:target forEvent:event];
/// 收集数据
}
@end
|
针对列表点击事件,以UITableView为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
@implementation UIScrollView (ClickKit)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[ClickKit swapMethod:[self class]
origMethod:@selector(setDelegate:)
newMethod:@selector(swizzle_setDelegate:)];
});
}
- (void)swizzle_setDelegate:(id<UIScrollViewDelegate>)delegate
{
[self swizzle_setDelegate:delegate];
if ([self isKindOfClass:[UITableView class]]) {
[(UITableView *)self swizzle_tableViewDidSelectRowAtIndexPathInClass:delegate];
}
else if ([self isKindOfClass:[UICollectionView class]]) {
[(UICollectionView *)self swizzle_collectionViewDidSelectRowAtIndexPathInClass:delegate];
}
}
@end
@implementation UITableView (ClickKit)
- (void)swizzle_tableViewDidSelectRowAtIndexPathInClass:(id)delegate
{
if ([delegate isKindOfClass:[UITableView class]]) {
return;
}
if ([ClickKit hasMethod:[delegate class] sel:@selector(tableView:didSelectRowAtIndexPath:)])
{
SEL swizSel = NSSelectorFromString(@"swizzle_didSelectRowAtIndexPath");
if ([ClickKit addMethod:[delegate class] sel:swizSel method:(IMP)swizzle_didSelectRowAtIndexPath]) {
[ClickKit swapMethod:[delegate class] origMethod:swizSel newMethod:@selector(tableView:didSelectRowAtIndexPath:)];
}
}
}
void swizzle_didSelectRowAtIndexPath(id self, SEL _cmd, id tableView, id indexPath)
{
SEL selector = NSSelectorFromString(@"swizzle_didSelectRowAtIndexPath");
((void(*)(id, SEL, id, id))objc_msgSend)(self, selector, tableView, indexPath);
/// 收集数据
}
@end
|
四、数据存储
埋点数据采用db方式进行数据存储,一般依据事件类型,db结构由启动表、pv表和event表组成。具体怎么组织数据结构,需要上报什么数据,需要和大数据沟通。
五、数据上报
上报策略:
1、采用实时上传和离线上传相结合的方式,wifi和4G模式下,pv采用实时上报的方式,事件是随着下一个pv同时上传。
2、离线上传:其它网络情况下只做存储处理。
这个策略也只是建议,具体还看业务需求。