环境集成iOS开发札记专业知识分享(iOS)

iOS 使用AOP统计打点

2016-09-21  本文已影响1812人  来宝

统计打点是 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];

3、上面涉及到的EventPlist是新创建的plist文件,设置方式如下:

B091368F-0F17-4671-9A5E-F6527BA5CD31.png

需要demo的同学麻烦打个赏(金额随便给),留个邮箱,我会发到你们邮箱里,谢谢支持!

上一篇 下一篇

猜你喜欢

热点阅读