iOS無埋點數(shù)據(jù)統(tǒng)計實驗

項目中需要統(tǒng)計的數(shù)據(jù)包括

1.在某個頁面的停留時間(針對UIViewController)
2.某個事件(method)觸發(fā)的次數(shù)
3.某個View的展示次數(shù)
(目前屬于試驗階段)

1.停留時間runtime swap viewDidAppear & viewDidDisAppear

#import "UIViewController+JHswizzling.h"
static NSDate *startDate;
@implementation UIViewController (JHswizzling)
+ (void)load{
    
    
    // 通過class_getInstanceMethod()函數(shù)從當(dāng)前對象中的method list獲取method結(jié)構(gòu)體呻待,如果是類方法就使用class_getClassMethod()函數(shù)獲取庸诱。
    Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidAppear:));
    Method toMethod = class_getInstanceMethod([self class], @selector(JH_swizzlingViewDidAppear));
    /**
     *  我們在這里使用class_addMethod()函數(shù)對Method Swizzling做了一層驗證,如果self沒有實現(xiàn)被交換的方法斟湃,會導(dǎo)致失敗熏迹。
     *  而且self沒有交換的方法實現(xiàn),但是父類有這個方法凝赛,這樣就會調(diào)用父類的方法注暗,結(jié)果就不是我們想要的結(jié)果了坛缕。
     *  所以我們在這里通過class_addMethod()的驗證,如果self實現(xiàn)了這個方法捆昏,class_addMethod()函數(shù)將會返回NO赚楚,我們就可以對其進(jìn)行交換了。
     */
    if (!class_addMethod([self class], @selector(JH_swizzlingViewDidAppear), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
        method_exchangeImplementations(fromMethod, toMethod);
    }
    
    // 通過class_getInstanceMethod()函數(shù)從當(dāng)前對象中的method list獲取method結(jié)構(gòu)體骗卜,如果是類方法就使用class_getClassMethod()函數(shù)獲取宠页。
    Method fromMethodDis = class_getInstanceMethod([self class], @selector(viewDidDisappear:));
    Method toMethodDis = class_getInstanceMethod([self class], @selector(JH_swizzlingViewDidDisAppear));

    if (!class_addMethod([self class], @selector(JH_swizzlingViewDidDisAppear), method_getImplementation(toMethodDis), method_getTypeEncoding(toMethodDis))) {
        method_exchangeImplementations(fromMethodDis, toMethodDis);
    }
}

// 我們自己實現(xiàn)的方法,也就是和self的viewDidAppear方法進(jìn)行交換的方法寇仓。
- (void)JH_swizzlingViewDidAppear{
    
    NSString *str = [NSString stringWithFormat:@"%@", self.class];
    // 我們在這里加一個判斷举户,將系統(tǒng)的UIViewController的對象剔除掉
    NSDictionary *data = [self getJsonData];
    if ([[data allKeys]containsObject:str]) {
        
        if(![str containsString:@"UI"]){
            startDate = [NSDate  date];
            NSLog(@"統(tǒng)計打點出現(xiàn) : %@ time : %@", [self getJsonData][str] ,startDate);
        }
    }
    
    [self JH_swizzlingViewDidAppear];
}

// 我們自己實現(xiàn)的方法,也就是和self的viewDidDisAppear方法進(jìn)行交換的方法焚刺。
- (void)JH_swizzlingViewDidDisAppear{
    NSString *str = [NSString stringWithFormat:@"%@", self.class];
    // 我們在這里加一個判斷敛摘,將系統(tǒng)的UIViewController的對象剔除掉
    NSDictionary *data = [self getJsonData];
    if ([[data allKeys]containsObject:str]) {
        if(![str containsString:@"UI"]){
            //計算時間差
            NSDate *endDate = [NSDate date];
            NSTimeInterval duration = [endDate timeIntervalSinceDate:startDate];
            NSLog(@"統(tǒng)計打點出現(xiàn) : %@ time : %f 時長", data[str] ,duration);
            //組合數(shù)據(jù)并存入數(shù)據(jù)庫
            NSDictionary *vcDic = @{@"viewControllerCodeName":str,
                                       @"viewControllerName":data[str],
                                       @"viewControllerTime":[NSString stringWithFormat:@"%f",duration],
                                       };
            [JH_AnalyseDataHelper _AnalyseWithData:vcDic withType:AnalyseTypeViewController];
            
        }
    }
    
    [self JH_swizzlingViewDidDisAppear];
}

-(NSDictionary *)getJsonData{
    NSString *filePath = [[NSBundle mainBundle]pathForResource:@"analyse" ofType:@"json"];
    NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:nil];
    
    return dic[@"viewController"];
}

2.某個事件(method)觸發(fā)的次數(shù),針對系統(tǒng)控件

#import "JHAnalyseControlAnalyseNode.h"

@implementation JHAnalyseControlAnalyseNode
+(void)load{
    Method JH_sendAction = class_getInstanceMethod([UIControl class], @selector(sendAction:to:forEvent:));
    class_addMethod([UIControl class], @selector(JHhook_sendAction:to:forEvent:), method_getImplementation(JH_sendAction), method_getTypeEncoding(JH_sendAction));
    method_setImplementation(JH_sendAction, class_getMethodImplementation([self class], @selector(JHhook_sendAction:to:forEvent:)));
    
    
}
/**
 替換的方法
 */
-(void)JHhook_sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event{
    
    NSString *methodName = NSStringFromSelector(action);
    NSString *className = [NSString stringWithUTF8String: object_getClassName(target)];
    UIControl *sender = (UIControl *)self;
    //第一層,視圖ClassName
    NSDictionary *data = [[JHAnalyseControlAnalyseNode class]getJsonData];
    if ([[data allKeys]containsObject:className]) {
        //第二層乳愉,Action
        NSDictionary *class = data[className];
        if([[class allKeys]containsObject:methodName]){
            NSDictionary *action = class[methodName];
            
            NSString *tag = [NSString stringWithFormat:@"%ld",sender.tag];
            if([[action allKeys]containsObject:tag]){
                NSDictionary *oneAction = action[tag];
                NSLog(@"mtthodName=%@,className=%@,classRealName=%@tag=%@",methodName,className,oneAction[@"name"],tag);
                //使用當(dāng)前時間表示最后操作時間
                NSDate *date = [NSDate date];
                
                NSTimeZone *zone = [NSTimeZone systemTimeZone];
                
                NSInteger interval = [zone secondsFromGMTForDate: date];
                
                NSDate *localeDate = [date  dateByAddingTimeInterval: interval];
                
                //組合數(shù)據(jù)并存入數(shù)據(jù)庫
                NSDictionary *eventDic = @{@"eventClass":className,
                                           @"eventCodeName":methodName,
                                           @"eventCount":@"1",
                                           @"eventDate":[NSString stringWithFormat:@"%@",localeDate],
                                           @"eventName":oneAction[@"name"],
                                           @"eventTag":tag,
                                           @"eventUser":@"jianghong",
                                           };
                [JH_AnalyseDataHelper _AnalyseWithData:eventDic withType:AnalyseTypeEvent];
            }
        }
    }
    
    [self JHhook_sendAction:action to:target forEvent:event];
    
}
+(NSDictionary *)getJsonData{
    NSString *filePath = [[NSBundle mainBundle]pathForResource:@"analyse" ofType:@"json"];
    NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:nil];
    
    return dic[@"event"];
}

配置的json文件(這個json文件最終將會配置到服務(wù)端作為下載數(shù)據(jù))

{
    "viewController":{
        "JHChatBaseController":"聊天主頁面",
        "JHMapLocationVC":"定位",
        "JHChildMessageVC":"消息",
        "JHChildFriendsVC":"好友",
        "JHNoteVC":"廣場",
        "JHSquareVC":"筆記"
    },
    "event":{
        "JHInputView":{
            "_additionButtonAction:":{
                "0":{
                    "class":"JHInputView",
                    "event":"_additionButtonAction:",
                    "tag":"0",
                    "name":"錄音"
                },
                "1":{
                    "class":"JHInputView",
                    "event":"_additionButtonAction:",
                    "tag":"1",
                    "name":"相冊"
                },
                "2":{
                    "class":"JHInputView",
                    "event":"_additionButtonAction:",
                    "tag":"2",
                    "name":"相機(jī)"
                },
                "3":{
                    "class":"JHInputView",
                    "event":"_additionButtonAction:",
                    "tag":"3",
                    "name":"定位"
                }
            },
            "_sendAction":{
                "0":{
                    "class":"JHInputView",
                    "event":"_sendAction",
                    "tag":"0",
                    "name":"發(fā)送信息"
                }
            }
        },
        "JHChatBaseCellVoice":{
            "onPlayButton:":{
                "0":{
                    "class":"JHChatBaseCellVoice",
                    "event":"onPlayButton:",
                    "tag":"0",
                    "name":"播放錄音"
                }
            }
        }
    }
}

CoreData數(shù)據(jù)庫操作

1.建立CoreDataModel對象



image.png

2.根據(jù)統(tǒng)計類型查詢數(shù)據(jù)庫更新數(shù)據(jù)庫

#import "JH_AnalyseDataHelper.h"
#define kManagedObjectContext [JH_ChatMessageManager sharedInstance].managedObjectContext
#define JH_EventAnalyseData @"EventAnalyseData"
#define JH_ViewControllerAnalyseData @"ViewControllerAnalyseData"

@implementation JH_AnalyseDataHelper
+(void)_AnalyseWithData:(NSDictionary *)data withType:(AnalyseType )analyseType{
    if (analyseType==AnalyseTypeViewController) {
        [self analyseVCWithData:data];
    }else if (analyseType == AnalyseTypeEvent){
        [self analyseEventWithData:data];
    }
    
    
}
/**
 分析統(tǒng)計頁面
 */
+(void)analyseVCWithData:(NSDictionary *)data{
    //判斷是新建還是更新
    NSArray *list = [self _searchViewControllerData];
    //創(chuàng)建對應(yīng)的類
    NSString *vcName = data[@"viewControllerName"];
    
    for (ViewControllerAnalyseData *vcModel in list) {
        if ([vcModel.viewControllerName isEqualToString:vcName]) {
            //更新時長數(shù)據(jù)
            vcModel.viewControllerTime = vcModel.viewControllerTime + [data[@"viewControllerTime"] floatValue];
            [[JH_ChatMessageManager sharedInstance] saveContext]; //保存
            return;
        }
    }
    //創(chuàng)建一個新的
    ViewControllerAnalyseData *vcModel = [NSEntityDescription insertNewObjectForEntityForName:JH_ViewControllerAnalyseData inManagedObjectContext:kManagedObjectContext];
    for (NSString *str in [data allKeys]) {
        if ([str isEqualToString:@"viewControllerTime"]) {
            float time = [data[str] floatValue];
            [vcModel setValue:@(time) forKey:str];
            continue;
        }
        [vcModel setValue:data[str] forKey:str];
    }
    
    [[JH_ChatMessageManager sharedInstance] saveContext]; //保存
}
/**
 分析統(tǒng)計事件
 */
+(void)analyseEventWithData:(NSDictionary *)data{
    //判斷是新建還是更新
    NSArray *list = [self _searchEventData];
    //創(chuàng)建對應(yīng)的類
    NSString *eventName = data[@"eventName"];
    
    for (EventAnalyseData *vcModel in list) {
        if ([vcModel.eventName isEqualToString:eventName]) {
            //更新點擊次數(shù)數(shù)據(jù)
            vcModel.eventCount = vcModel.eventCount + [data[@"eventCount"] integerValue];
            [[JH_ChatMessageManager sharedInstance] saveContext]; //保存
            return;
        }
    }
    
    //創(chuàng)建一個新的
    EventAnalyseData *eventModel = [NSEntityDescription insertNewObjectForEntityForName:JH_EventAnalyseData inManagedObjectContext:kManagedObjectContext];
    
    for (NSString *str in [data allKeys]) {
        if ([str isEqualToString:@"eventCount"]) {
            NSInteger count = [data[str] integerValue];
            [eventModel setValue:@(count) forKey:str];
            
            continue;
        }
        [eventModel setValue:data[str] forKey:str];
    }
    
    [[JH_ChatMessageManager sharedInstance] saveContext]; //保存
}

#pragma mark - 查詢數(shù)據(jù)(暫時使用全部搜索)
+(NSArray *)_searchViewControllerData{
    /**
     數(shù)據(jù)查詢數(shù)據(jù)(全部)
     */
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    
    NSEntityDescription *entity = [NSEntityDescription entityForName:JH_ViewControllerAnalyseData
                                   
                                              inManagedObjectContext:kManagedObjectContext];
    
    [request setEntity:entity];
    
    NSError *error = nil;
    
    NSArray *objectResults = [kManagedObjectContext
                              
                              executeFetchRequest:request
                              
                              error:&error];
    return objectResults;
    
}
+(NSArray *)_searchEventData{
    /**
     數(shù)據(jù)查詢數(shù)據(jù)(全部)
     */
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    
    NSEntityDescription *entity = [NSEntityDescription entityForName:JH_EventAnalyseData
                                   
                                              inManagedObjectContext:kManagedObjectContext];
    
    [request setEntity:entity];
    
    NSError *error = nil;
    
    NSArray *objectResults = [kManagedObjectContext
                              
                              executeFetchRequest:request
                              
                              error:&error];
    return objectResults;
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末兄淫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蔓姚,更是在濱河造成了極大的恐慌捕虽,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坡脐,死亡現(xiàn)場離奇詭異泄私,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)备闲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門晌端,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人恬砂,你說我怎么就攤上這事咧纠。” “怎么了泻骤?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵漆羔,是天一觀的道長。 經(jīng)常有香客問我狱掂,道長演痒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任趋惨,我火速辦了婚禮鸟顺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘器虾。我一直安慰自己诊沪,他們只是感情好养筒,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著端姚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挤悉。 梳的紋絲不亂的頭發(fā)上渐裸,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機(jī)與錄音装悲,去河邊找鬼昏鹃。 笑死,一個胖子當(dāng)著我的面吹牛诀诊,可吹牛的內(nèi)容都是我干的洞渤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼属瓣,長吁一口氣:“原來是場噩夢啊……” “哼载迄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抡蛙,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤护昧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后粗截,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惋耙,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年熊昌,在試婚紗的時候發(fā)現(xiàn)自己被綠了绽榛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡婿屹,死狀恐怖灭美,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情选泻,我是刑警寧澤冲粤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站页眯,受9級特大地震影響梯捕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窝撵,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一傀顾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碌奉,春花似錦短曾、人聲如沸寒砖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哩都。三九已至,卻和暖如春婉徘,著一層夾襖步出監(jiān)牢的瞬間漠嵌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工盖呼, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留儒鹿,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓几晤,卻偏偏與公主長得像约炎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蟹瘾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理圾浅,服務(wù)發(fā)現(xiàn),斷路器热芹,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • 本篇文章是基于 網(wǎng)易樂得無埋點數(shù)據(jù)SDK 總結(jié)而成贱傀。負(fù)責(zé)無埋點數(shù)據(jù)收集 SDK 的開發(fā)已經(jīng)有半年多了,期間在組內(nèi)進(jìn)...
    zerygao閱讀 48,497評論 121 388
  • 1.背景說明 最近公司需要對用戶行為進(jìn)行深層次的數(shù)據(jù)分析和挖掘伊脓,需要用戶詳細(xì)的操作數(shù)據(jù)府寒,想走無埋點的數(shù)據(jù)上報方式。...
    木魚鐘閱讀 804評論 1 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,139評論 25 707
  • Jack張了了閱讀 745評論 0 1