统计打点是 App 开发里很重要的一个环节,App 的运行状态、用户的各种行为等都需要打点,有不少关于统计的第三方库(如友盟统计)。但是,如果要求在整个项目的所有button里统计用户的点击事件,假如一个项目里面有1000个button,你就要设1000个地方设置统计代码,显然不科学。
有没有一种办法在一个地方进行统计打点,检测整个应用的点击事件?
方案一:使用Runtime的方式追踪点击的按钮
特点:需要对每个button进行tag编号,对手势点击、tableView的点击要单独配置,比较繁琐
方案二:使用面向切面编程AOP对按钮或者页面进行追踪(无需在任何详情页面中做相应配置)
特点:
1、在不修改源代码的情况下,通过运行时给程序添加统一功能的技术,可以用作日志记录,性能统计等
2、无需对每个button进行tag编号,创建button后只需在新建的plist中配置button对应的方法名和对应的事件 ID就行
3、适用于Tap点击手势,使用时设置事件ID,和button的使用方法一样
4、button不支持直接在block里面写事件的方式,但可以在block里面调用方法或者需要统一写成下面的方式
[button addTarget:self action:@selector(click)forControlEvents:UIControlEventTouchUpInside];
5、适用于tableview的didSelectRowAtIndexPath点击事件,可获取tableView对应的类名、section和row
6、如果是统计tableview的点击事件,根据需要在获取到section和row后加个判断埋点统计
if (section == 0 && row == 1) {
[MobClick event:eventID];
}
7、如果有特殊需求:某个按钮登录前和登录后记录的事件不一样,需要加判断
if ([eventID isEqualToString:@"xxx"]) {
[EJServiceUserInfo isLogin]?[MobClick event:eventID]:[MobClick event:@"???"];
}else{
[MobClick event:eventID];
}
以下是具体代码:
(不得不吐槽一下,网上很多博客文章都是转载的,很少有能直接运行的,研究了一整天才弄出来)
这里用到了第三方库:Aspects,用cocoaPods进行集成 pod 'Aspects'
1、创建一个继承与NSObject的EJAspectManager类
EJAspectManager.h
@interface EJAspectManager : NSObject
+(void)trackAspectHooks;
@end
EJAspectManager.m
#import "EJAspectManager.h"
#import "Aspects/Aspects.h"
@implementation EJAspectManager
+(void)trackAspectHooks{
[EJAspectManager trackViewAppear];
[EJAspectManager trackBttonEvent];
}
#pragma mark -- 监控统计用户进入此界面的时长,频率等信息
+ (void)trackViewAppear{
[UIViewController aspect_hookSelector:@selector(viewWillAppear:)
withOptions:AspectPositionBefore
usingBlock:^(id<AspectInfo> info){
//用户统计代码写在此处
DDLogDebug(@"[打点统计]:%@ viewWillAppear",NSStringFromClass([info.instance class]));
NSString *className = NSStringFromClass([info.instance class]);
DLog(@"className-->%@",className);
[MobClick beginLogPageView:className];//(className为页面名称
}
error:NULL];
[UIViewController aspect_hookSelector:@selector(viewWillDisappear:)
withOptions:AspectPositionBefore
usingBlock:^(id<AspectInfo> info){
//用户统计代码写在此处
DDLogDebug(@"[打点统计]:%@ viewWillDisappear",NSStringFromClass([info.instance class]));
NSString *className = NSStringFromClass([info.instance class]);
DLog(@"className-->%@",className);
[MobClick endLogPageView:className];
}
error:NULL];
//other hooks ... goes here
//...
}
#pragma mark --- 监控button的点击事件
+ (void)trackBttonEvent{
__weak typeof(self) ws = self;
//设置事件统计
//放到异步线程去执行
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//读取配置文件,获取需要统计的事件列表
NSString *path = [[NSBundle mainBundle] pathForResource:@"EventList" ofType:@"plist"];
NSDictionary *eventStatisticsDict = [[NSDictionary alloc] initWithContentsOfFile:path];
for (NSString *classNameString in eventStatisticsDict.allKeys) {
//使用运行时创建类对象
const char * className = [classNameString UTF8String];
//从一个字串返回一个类
Class newClass = objc_getClass(className);
NSArray *pageEventList = [eventStatisticsDict objectForKey:classNameString];
for (NSDictionary *eventDict in pageEventList) {
//事件方法名称
NSString *eventMethodName = eventDict[@"MethodName"];
SEL seletor = NSSelectorFromString(eventMethodName);
NSString *eventId = eventDict[@"EventId"];
[ws trackEventWithClass:object_getClass(newClass) selector:seletor eventID:eventId];
[ws trackTableViewEventWithClass:object_getClass(newClass) selector:seletor eventID:eventId];
[ws trackParameterEventWithClass:object_getClass(newClass) selector:seletor eventID:eventId];
}
}
});
}
#pragma mark -- 监控button和tap点击事件(不带参数)
+ (void)trackEventWithClass:(Class)klass selector:(SEL)selector eventID:(NSString*)eventID{
[klass aspect_hookSelector:selector withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) {
NSString *className = NSStringFromClass([aspectInfo.instance class]);
NSLog(@"className--->%@",className);
NSLog(@"event----->%@",eventID);
if ([eventID isEqualToString:@"xxx"]) {
[EJServiceUserInfo isLogin]?[MobClick event:eventID]:[MobClick event:@"???"];
}else{
[MobClick event:eventID];
}
} error:NULL];
}
#pragma mark -- 监控button和tap点击事件(带参数)
+ (void)trackParameterEventWithClass:(Class)klass selector:(SEL)selector eventID:(NSString*)eventID{
[klass aspect_hookSelector:selector withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo,UIButton *button) {
NSLog(@"button---->%@",button);
NSString *className = NSStringFromClass([aspectInfo.instance class]);
NSLog(@"className--->%@",className);
NSLog(@"event----->%@",eventID);
} error:NULL];
}
#pragma mark -- 监控tableView的点击事件
+ (void)trackTableViewEventWithClass:(Class)klass selector:(SEL)selector eventID:(NSString*)eventID{
[klass aspect_hookSelector:selector withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo,NSSet *touches, UIEvent *event) {
NSString *className = NSStringFromClass([aspectInfo.instance class]);
NSLog(@"className--->%@",className);
NSLog(@"event----->%@",eventID);
NSLog(@"section---->%@",[event valueForKeyPath:@"section"]);
NSLog(@"row---->%@",[event valueForKeyPath:@"row"]);
NSInteger section = [[event valueForKeyPath:@"section"]integerValue];
NSInteger row = [[event valueForKeyPath:@"row"]integerValue];
//统计事件
if (section == 0 && row == 1) {
[MobClick event:eventID];
}
} error:NULL];
}
@end
2、这样我们在appDelegate里面直接调用下面的代码就可以达到全局统计的目的了!
[EJAspectManager trackAspectHooks];