統(tǒng)計打點是 App 開發(fā)里很重要的一個環(huán)節(jié)藻肄,App 的運行狀態(tài)潮峦、用戶的各種行為等都需要打點或舞,有不少關(guān)于統(tǒng)計的第三方庫(如友盟統(tǒng)計)兴喂。但是蔼囊,如果要求在整個項目的所有button里統(tǒng)計用戶的點擊事件,假如一個項目里面有1000個button衣迷,你就要設(shè)1000個地方設(shè)置統(tǒng)計代碼畏鼓,顯然不科學(xué)。
有沒有一種辦法在一個地方進行統(tǒng)計打點壶谒,檢測整個應(yīng)用的點擊事件云矫?
方案一:使用Runtime的方式追蹤點擊的按鈕
特點:需要對每個button進行tag編號,對手勢點擊汗菜、tableView的點擊要單獨配置让禀,比較繁瑣
方案二:使用面向切面編程AOP對按鈕或者頁面進行追蹤(無需在任何詳情頁面中做相應(yīng)配置)
特點:
1挑社、在不修改源代碼的情況下,通過運行時給程序添加統(tǒng)一功能的技術(shù)巡揍,可以用作日志記錄痛阻,性能統(tǒng)計等
2、無需對每個button進行tag編號腮敌,創(chuàng)建button后只需在新建的plist中配置button對應(yīng)的方法名和對應(yīng)的事件 ID就行
3录平、適用于Tap點擊手勢,使用時設(shè)置事件ID缀皱,和button的使用方法一樣
4斗这、button不支持直接在block里面寫事件的方式,但可以在block里面調(diào)用方法或者需要統(tǒng)一寫成下面的方式
[button addTarget:self action:@selector(click)forControlEvents:UIControlEventTouchUpInside];
5啤斗、適用于tableview的didSelectRowAtIndexPath點擊事件表箭,可獲取tableView對應(yīng)的類名、section和row
6钮莲、如果是統(tǒng)計tableview的點擊事件免钻,根據(jù)需要在獲取到section和row后加個判斷埋點統(tǒng)計
if (section == 0 && row == 1) {
[MobClick event:eventID];
}
7、如果有特殊需求:某個按鈕登錄前和登錄后記錄的事件不一樣崔拥,需要加判斷
if ([eventID isEqualToString:@"xxx"]) {
[EJServiceUserInfo isLogin]?[MobClick event:eventID]:[MobClick event:@"???"];
}else{
[MobClick event:eventID];
}
以下是具體代碼:
(不得不吐槽一下极舔,網(wǎng)上很多博客文章都是轉(zhuǎn)載的,很少有能直接運行的链瓦,研究了一整天才弄出來)
這里用到了第三方庫:Aspects,用cocoaPods進行集成 pod 'Aspects'
1拆魏、創(chuàng)建一個繼承與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 -- 監(jiān)控統(tǒng)計用戶進入此界面的時長,頻率等信息
+ (void)trackViewAppear{
[UIViewController aspect_hookSelector:@selector(viewWillAppear:)
withOptions:AspectPositionBefore
usingBlock:^(id<AspectInfo> info){
//用戶統(tǒng)計代碼寫在此處
DDLogDebug(@"[打點統(tǒng)計]:%@ 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){
//用戶統(tǒng)計代碼寫在此處
DDLogDebug(@"[打點統(tǒng)計]:%@ 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 --- 監(jiān)控button的點擊事件
+ (void)trackBttonEvent{
__weak typeof(self) ws = self;
//設(shè)置事件統(tǒng)計
//放到異步線程去執(zhí)行
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//讀取配置文件慈俯,獲取需要統(tǒng)計的事件列表
NSString *path = [[NSBundle mainBundle] pathForResource:@"EventList" ofType:@"plist"];
NSDictionary *eventStatisticsDict = [[NSDictionary alloc] initWithContentsOfFile:path];
for (NSString *classNameString in eventStatisticsDict.allKeys) {
//使用運行時創(chuàng)建類對象
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 -- 監(jiān)控button和tap點擊事件(不帶參數(shù))
+ (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 -- 監(jiān)控button和tap點擊事件(帶參數(shù))
+ (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 -- 監(jiān)控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];
//統(tǒng)計事件
if (section == 0 && row == 1) {
[MobClick event:eventID];
}
} error:NULL];
}
@end
2渤刃、這樣我們在appDelegate里面直接調(diào)用下面的代碼就可以達到全局統(tǒng)計的目的了!
[EJAspectManager trackAspectHooks];