自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

iOS 無侵入埋點(diǎn)組件總結(jié)

移動(dòng)開發(fā) iOS
無埋點(diǎn)并不是不需要埋點(diǎn),更準(zhǔn)確的說應(yīng)該是“全埋”, 前端的任意一個(gè)事件都被綁定一個(gè)標(biāo)識(shí),所有的事件都別記錄下來。通過定期上傳記錄文件,配合文件解析,解析出來我們想要的數(shù)據(jù), 并生成可視化報(bào)告 , 因此實(shí)現(xiàn)“無埋點(diǎn)”統(tǒng)計(jì)。

[[416572]]

本文轉(zhuǎn)載自微信公眾號(hào)「網(wǎng)羅開發(fā)」,作者Perry_6。轉(zhuǎn)載本文請(qǐng)聯(lián)系網(wǎng)羅開發(fā)公眾號(hào)。

一. 埋點(diǎn)方案

代碼埋點(diǎn)

由開發(fā)人員在觸發(fā)事件的具體方法里,添加多行代碼把需要上傳的參數(shù)上報(bào)至服務(wù)端。

可視化埋點(diǎn)

根據(jù)標(biāo)識(shí)來識(shí)別每一個(gè)事件, 針對(duì)指定的事件進(jìn)行取參埋點(diǎn)。而事件的標(biāo)識(shí)與參數(shù)信息都寫在配置表中,通過動(dòng)態(tài)下發(fā)配置表來實(shí)現(xiàn)埋點(diǎn)統(tǒng)計(jì)。

無埋點(diǎn)

無埋點(diǎn)并不是不需要埋點(diǎn),更準(zhǔn)確的說應(yīng)該是“全埋”, 前端的任意一個(gè)事件都被綁定一個(gè)標(biāo)識(shí),所有的事件都別記錄下來。通過定期上傳記錄文件,配合文件解析,解析出來我們想要的數(shù)據(jù), 并生成可視化報(bào)告 , 因此實(shí)現(xiàn)“無埋點(diǎn)”統(tǒng)計(jì)。

二. 方案選擇

通常業(yè)務(wù)都需要加埋點(diǎn)統(tǒng)計(jì)事件,但在每個(gè)業(yè)務(wù)類里埋點(diǎn)會(huì)導(dǎo)致每個(gè)頁面內(nèi)耦合了大量的無關(guān)業(yè)務(wù)的埋點(diǎn)代碼使得代碼不夠整潔,所以放棄了代碼埋點(diǎn)。

考慮到無埋點(diǎn)成本較高,后期解析也復(fù)雜,選擇了可視化埋點(diǎn),即通過配置事件唯一標(biāo)識(shí),設(shè)置需要埋點(diǎn)分析的業(yè)務(wù)。

2.1 實(shí)現(xiàn)可視化埋點(diǎn)核心問題

  • 封裝埋點(diǎn)組件,降低耦合
  • 如何實(shí)現(xiàn)后臺(tái)配置唯一標(biāo)識(shí)
  • 埋點(diǎn)上報(bào)

2.2 針對(duì)第一個(gè)問題想到的方案如下:

  • 每個(gè)業(yè)務(wù)頁面添加一個(gè)埋點(diǎn)類,單獨(dú)將埋點(diǎn)的方法提取到這個(gè)類中。
  • 利用 Runtime 在底層進(jìn)行方法攔截,從而添加埋點(diǎn)代碼。

結(jié)合AOP的核心思想:將應(yīng)用程序中的業(yè)務(wù)邏輯同對(duì)其提供支持的通用服務(wù)進(jìn)行分離,最后采用了第2種方案。

2.3 配置唯一標(biāo)識(shí)問題

唯一標(biāo)識(shí)的組成方式主要是又 target + action 來確定, 即任何一個(gè)事件都存在一個(gè) target 與 action。在此引入 AOP 編程,AOP(Aspect-Oriented-Programming) 即面向切面編程的思想,基于 Runtime 的 Method Swizzling 能力,來 hook 相應(yīng)的方法,從而在 hook 方法中進(jìn)行統(tǒng)一的埋點(diǎn)處理。例如所有的按鈕被點(diǎn)擊時(shí),都會(huì)觸發(fā) UIApplication 的 sendAction 方法,我們 hook 這個(gè)方法,即可攔截所有按鈕的點(diǎn)擊事件。

2.3.1 唯一標(biāo)識(shí)(viewPath)的獲?。?/strong>

整個(gè) APP 的視圖結(jié)構(gòu)可以看成是一顆樹(viewTree),樹的根節(jié)點(diǎn)就是 UIWindow,樹的枝干由 UIViewController 及 UIView 組成,樹的葉節(jié)點(diǎn)都是由 UIView 組成。

那么在 viewTree 中用什么信息來表示其中任意一個(gè) view 的位置呢?很容易想到的就是使用目標(biāo) view到根之間的每個(gè)節(jié)點(diǎn)的深度(層次)組成一個(gè)路徑,而節(jié)點(diǎn)的深度(層次)是指此節(jié)點(diǎn)在父節(jié)點(diǎn)中的 index。這樣確實(shí)能夠唯一的表示此 view 了,但是有一個(gè)缺點(diǎn):它的可讀性很差。因此在此基礎(chǔ)上又增加了每個(gè)節(jié)點(diǎn)的名稱,節(jié)點(diǎn)的名稱由當(dāng)前節(jié)點(diǎn)的 view 的類名來表示。同時(shí)在開頭都添加了一個(gè)頁面名稱作為標(biāo)識(shí)。

因此,在 viewTree 中,由一個(gè) view 到根節(jié)點(diǎn)之間的每個(gè)節(jié)點(diǎn)的名稱與深度(層次)共同組成的信息構(gòu)成了此 view 的 viewPath。另外,由于在做 view 的統(tǒng)計(jì)分析時(shí),都是以頁面為單位的,因此 SDK 在生成 viewPath 時(shí),只到 view 所在的 UIViewController 級(jí)別,而非根部的 UIWindow。這樣做也在一定程度上減少了 viewPath 的長(zhǎng)度。

UITableView 和 UICollectionView 的樹級(jí)關(guān)系沒有到每個(gè)具體的 cell,避免產(chǎn)生很多無用的 id,而是將 indexpath 作為描述信息傳入。實(shí)現(xiàn)邏輯如下圖:

2.3.4 唯一標(biāo)識(shí)的作用主要分為兩個(gè)部分

  • 事件的鎖定

事件的鎖定主要是靠 “事件唯一標(biāo)識(shí)符”來鎖定,而事件的唯一標(biāo)識(shí)是由我們寫入配置表中的。

  • 埋點(diǎn)數(shù)據(jù)的上報(bào)。

埋點(diǎn)數(shù)據(jù)的數(shù)據(jù)又分為兩種類型: 固定數(shù)據(jù)與可變的業(yè)務(wù)數(shù)據(jù), 而固定數(shù)據(jù)我們可以直接寫到配置表中, 通過唯一標(biāo)識(shí)來獲取。而對(duì)于業(yè)務(wù)數(shù)據(jù),數(shù)據(jù)是有持有者的, 例如我們 Controller 的一個(gè)屬性值, 或者數(shù)據(jù)在 Model 的某一個(gè)層級(jí)。就可以通過 KVC 的的方式來遞歸獲取該屬性的值來取到業(yè)務(wù)數(shù)據(jù)。

2.4 埋點(diǎn)上報(bào)

自定義埋點(diǎn)上報(bào)數(shù)據(jù)類型,上報(bào)到 elastic,后臺(tái)進(jìn)行數(shù)據(jù)分析

三. 實(shí)現(xiàn)部分

3.1 SDK 架構(gòu)

3.2 技術(shù)原理

3.2.1 Method-Swizzling

OC 中的方法調(diào)用其實(shí)是向一個(gè)對(duì)象發(fā)送消息 ,利用 OC 的動(dòng)態(tài)性可以實(shí)現(xiàn)方法的交換。

  • 用 method_exchangeImplementations 方法來交換兩個(gè)方法中的IMP
  • 用 class_replaceMethod 方法來替換類的方法,
  • 用 method_setImplementation 方法來直接設(shè)置某個(gè)方法的 IMP

3.2.2 Target-Action

按鈕的點(diǎn)擊事件,UIControl 會(huì)調(diào)用 sendAction:to:forEvent: 來將行為消息轉(zhuǎn)發(fā)到 UIApplication,再由 UIApplication 調(diào)用其 sendAction:to:fromSender:forEvent: 方法來將消息分發(fā)到指定的 target 上。

3.3 分析及實(shí)現(xiàn)

3.3.1 需要添加埋點(diǎn)統(tǒng)計(jì)的地方

  • button 相關(guān)的點(diǎn)擊事件
  • 頁面進(jìn)入、頁面推出
  • tableView 的點(diǎn)擊
  • collectionView 的點(diǎn)擊
  • 手勢(shì)相關(guān)事件

3.3.2 分析

  • 對(duì)于用戶交互的操作,我們使用 runtime 對(duì)應(yīng)的方法 hook 下 sendAction:to:forEvent: 便可以得到進(jìn)行的交互操作。這個(gè)方法對(duì) UIControl 及繼承 UIControl 的子類對(duì)象有效,如:UIButton、UISlider 等。
  • 對(duì)于 UIViewController,hook 下 ViewDidAppear: 這個(gè)方法知道哪個(gè)頁面顯示了就足夠了。
  • 對(duì)于 tableview 及 collectionview,我們 hook下setDelegate: 方法。檢測(cè)其有沒有實(shí)現(xiàn)對(duì)應(yīng)的點(diǎn)擊代理,因?yàn)? tableView:didSelectRowAtIndexPath: 及 collectionView:didSelectItemAtIndexPath: 是 option 的不是必須要實(shí)現(xiàn)的。
  • 對(duì)于手勢(shì),我們?cè)趧?chuàng)建的時(shí)候進(jìn)行 hook,方法為 initWithTarget:action:。

3.3.3 實(shí)現(xiàn)原理

用運(yùn)行時(shí)方法替換方法實(shí)現(xiàn)無侵入的埋點(diǎn)方法。

實(shí)現(xiàn)原理圖:

具體實(shí)現(xiàn)方法:

創(chuàng)建一個(gè)運(yùn)行時(shí)方法替換類 HGMethodSwizzingTool,實(shí)現(xiàn)替換的方法 `swizzingForClass: originalSel: swizzingSel:``

  1. #import "LZMethodSwizzingTool.h" 
  2. #import <objc/runtime.h> 
  3.  
  4. @implementation LZMethodSwizzingTool 
  5.  
  6. + (void)swizzingForClass:(Class)cls originalSel:(SEL)originalSelector swizzingSel:(SEL)swizzingSelector { 
  7.     Class class = cls; 
  8.     Method originalMethod = class_getInstanceMethod(class, originalSelector); 
  9.     Method swizzingMethod = class_getInstanceMethod(class, swizzingSelector); 
  10.     BOOL addMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzingMethod), method_getTypeEncoding(swizzingMethod)); 
  11.     if (addMethod) { 
  12.         class_replaceMethod(class, swizzingSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); 
  13.     } else { 
  14.         method_exchangeImplementations(originalMethod, swizzingMethod); 
  15.     } 
  16. @end 

這個(gè)方法利用運(yùn)行時(shí) method_exchangeImplementations 進(jìn)行交換,當(dāng)原方法被調(diào)用時(shí),就會(huì) hook 到指定的新方法去執(zhí)行。

3.3.4 埋點(diǎn)分類實(shí)現(xiàn)

1. UIViewController+Track(頁面進(jìn)入、頁面推出)

  1. @implementation UIViewController (Track) 
  2.  
  3. + (void)initialize { 
  4.     static dispatch_once_t onceToken; 
  5.     dispatch_once(&onceToken, ^{ 
  6.         SEL originalWillAppearSelector = @selector(viewWillAppear:); 
  7.         SEL swizzingWillAppearSelector = @selector(hg_viewWillAppear:); 
  8.         [LZMethodSwizzingTool swizzingForClass:[self class] originalSel:originalWillAppearSelector swizzingSel:swizzingWillAppearSelector]; 
  9.  
  10.         SEL originalWillDisappearSel = @selector(viewWillDisappear:); 
  11.         SEL swizzingWillDisappearSel = @selector(hg_viewWillDisappear:); 
  12.         [LZMethodSwizzingTool swizzingForClass:[self class] originalSel:originalWillDisappearSel swizzingSel:swizzingWillDisappearSel]; 
  13.  
  14.         SEL originalDidLoadSel = @selector(viewDidLoad); 
  15.         SEL swizzingDidLoadSel = @selector(hg_viewDidLoad); 
  16.         [LZMethodSwizzingTool swizzingForClass:[self class] originalSel:originalDidLoadSel swizzingSel:swizzingDidLoadSel]; 
  17.     }); 
  18.  
  19. - (void)hg_viewWillAppear:(BOOL)animated { 
  20.     [self hg_viewWillAppear:animated]; 
  21.  
  22.     //埋點(diǎn)實(shí)現(xiàn)區(qū)域 
  23.     [self dataTrack:@"viewWillAppear"]; 
  24.  
  25. - (void)hg_viewWillDisappear:(BOOL)animated { 
  26.     [self hg_viewWillDisappear:animated]; 
  27.  
  28.     //埋點(diǎn)實(shí)現(xiàn)區(qū)域 
  29.     [self dataTrack:@"viewWillDisappear"]; 
  30.  
  31. - (void)hg_viewDidLoad { 
  32.     [self hg_viewDidLoad]; 
  33.  
  34.     //埋點(diǎn)實(shí)現(xiàn)區(qū)域 
  35.     [self dataTrack:@"viewDidLoad"]; 
  36.  
  37. - (void)dataTrack:(NSString *)methodName { 
  38.     NSString *identifier = [NSString stringWithFormat:@"%@/%@",[[LZFindVCManager currentViewController] class],methodName]; 
  39.     NSDictionary *eventDict = [[[LZDataTrackTool shareInstance].trackData objectForKey:@"ViewController"] objectForKey:identifier]; 
  40.     if (eventDict) { 
  41.         NSDictionary *useDefind = [eventDict objectForKey:@"userDefined"]; 
  42.         //預(yù)留參數(shù)配置,以后拓展 
  43.         NSDictionary *param = [eventDict objectForKey:@"eventParam"]; 
  44.         __block NSMutableDictionary *eventParam = [NSMutableDictionary dictionaryWithCapacity:0]; 
  45.         [param enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) { 
  46.             //在此處進(jìn)行屬性獲取 
  47.             id value = [LZCaptureTool captureVarforInstance:self varName:key]; 
  48.             if (key && value) { 
  49.                 [eventParam setObject:value forKey:key]; 
  50.             } 
  51.         }]; 
  52.         if (eventParam.count) { 
  53.             NSLog(@"identifier:%@-------useDefind:%@----eventParam:%@",identifier,useDefind,eventParam); 
  54.         } 
  55.     } 
  56. @end 

Category 在 +openTrackSelector() 方法里使用了 HGMethodSwizzingTool 進(jìn)行方法替換,在替換的方法里執(zhí)行需要埋點(diǎn)的方法 - (void)dataTrack:(NSString *)methodName 實(shí)現(xiàn)埋點(diǎn)。這樣每個(gè) UIViewController 生命周期到了 ViewWillAppear 都會(huì)執(zhí)行埋點(diǎn)的方法。

在這里,我們是通過類名 NSStringFromClass([self class]) 來區(qū)分不同的控制器的。

2. UIControl+Track(button相關(guān)的點(diǎn)擊事件)

  1. @implementation UIControl (Track) 
  2.  
  3. + (void)initialize { 
  4.     static dispatch_once_t onceToken; 
  5.     dispatch_once(&onceToken, ^{ 
  6.         SEL originalSelector = @selector(sendAction:to:forEvent:); 
  7.         SEL swizzingSelector = @selector(hg_sendAction:to:forEvent:); 
  8.         [LZMethodSwizzingTool swizzingForClass:[self class] originalSel:originalSelector swizzingSel:swizzingSelector]; 
  9.     }); 
  10.  
  11. - (void)hg_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { 
  12.     [self hg_sendAction:action to:target forEvent:event]; 
  13.  
  14.     //埋點(diǎn)實(shí)現(xiàn)區(qū)域==== 
  15.     //頁面/方法名/tag用來區(qū)分不同的點(diǎn)擊事件 
  16.     NSString *identifier = [NSString stringWithFormat:@"%@/%@/%@", [target class], NSStringFromSelector(action),@(self.tag)]; 
  17.     if ([target isKindOfClass:[UIView class]]) { 
  18.         UIView *view = (id)[target superview]; 
  19.         while (view.nextResponder) { 
  20.             identifier =[NSString stringWithFormat:@"%@/%@",NSStringFromClass(view.class),identifier]; 
  21.             if ([view.class isSubclassOfClass:[UIViewController class]]) { 
  22.                 break; 
  23.             } 
  24.             view = (id)view.nextResponder; 
  25.         } 
  26.     } 
  27.  
  28.     NSDictionary *eventDict = [[[LZDataTrackTool shareInstance].trackData objectForKey:@"Action"] objectForKey:identifier]; 
  29.     if (eventDict) { 
  30.         NSDictionary *useDefind = [eventDict objectForKey:@"userDefined"]; 
  31.         //預(yù)留參數(shù)配置,以后拓展 
  32.         NSDictionary *param = [eventDict objectForKey:@"eventParam"]; 
  33.         __block NSMutableDictionary *eventParam = [NSMutableDictionary dictionaryWithCapacity:0]; 
  34.         [param enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) { 
  35.             //在此處進(jìn)行屬性獲取 
  36.             id value = [LZCaptureTool captureVarforInstance:target varName:key]; 
  37.             if (key && value) { 
  38.                 [eventParam setObject:value forKey:key]; 
  39.             } 
  40.         }]; 
  41.  
  42.         NSLog(@"useDefind:%@----eventParam:%@",useDefind,eventParam); 
  43.     } 
  44.  
  45. // UIView 分類 
  46. - (NSString *)obtainSameSuperViewSameClassViewTreeIndexPat 
  47.     NSString *classStr = NSStringFromClass([self class]); 
  48.     //cell的子view 
  49.     //UITableView 特殊的superview (UITableViewContentView) 
  50.     //UICollectionViewCell 
  51.     BOOL shouldUseSuperView = 
  52.     ([classStr isEqualToString:@"UITableViewCellContentView"]) || 
  53.     ([[self.superview class] isKindOfClass:[UITableViewCell class]])|| 
  54.     ([[self.superview class] isKindOfClass:[UICollectionViewCell class]]); 
  55.     if (shouldUseSuperView) { 
  56.         return [self obtainIndexPathByView:self.superview]; 
  57.     }else { 
  58.         return [self obtainIndexPathByView:self]; 
  59.     } 
  60.  
  61. - (NSString *)obtainIndexPathByView:(UIView *)view 
  62.     NSInteger viewTreeNodeDepth = NSIntegerMin; 
  63.     NSInteger sameViewTreeNodeDepth = NSIntegerMin; 
  64.  
  65.     NSString *classStr = NSStringFromClass([view class]); 
  66.  
  67.     NSMutableArray *sameClassArr = [[NSMutableArray alloc]init]; 
  68.     //所處父view的全部subviews根節(jié)點(diǎn)深度 
  69.     for (NSInteger index =0; index < view.superview.subviews.countindex ++) { 
  70.         //同類型 
  71.         if  ([classStr isEqualToString:NSStringFromClass([view.superview.subviews[index] class])]){ 
  72.             [sameClassArr addObject:view.superview.subviews[index]]; 
  73.         } 
  74.         if (view == view.superview.subviews[index]) { 
  75.             viewTreeNodeDepth = index
  76.             break; 
  77.         } 
  78.     } 
  79.     //所處父view的同類型subviews根節(jié)點(diǎn)深度 
  80.     for (NSInteger index =0; index < sameClassArr.countindex ++) { 
  81.         if (view == sameClassArr[index]) { 
  82.             sameViewTreeNodeDepth = index
  83.             break; 
  84.         } 
  85.     } 
  86.     return [NSString stringWithFormat:@"%ld",sameViewTreeNodeDepth]; 
  87.  
  88. @end 

找到點(diǎn)擊事件的方法 sendAction:to:forEvent:,然后在 +openTrackSelector() 方法里使用 HGMethodSwizzingTool 替換新的方法。

和 UIViewController 生命周期埋點(diǎn)不同的是,一個(gè)類中可能有許多不同的 UIButton 子類,相同的 UIButton 子類在不同的視圖中的埋點(diǎn)也要區(qū)分出來,所以我們通過 NSStringFromClass([target class]) + NSStringFromSelector(action) 來區(qū)別,即類名加方法名的格式作為唯一標(biāo)識(shí)。

tableView、collectionView、手勢(shì)的點(diǎn)擊事件與上述實(shí)現(xiàn)方法類似。

3.3.5 埋點(diǎn)配置文件

埋點(diǎn)配置文件通過唯一標(biāo)識(shí)鎖定事件,可以使用 json 文件或 plist 文件,Demo 里就隨便寫了一些測(cè)試數(shù)據(jù),LZDataTrack.json 是直接放在了項(xiàng)目資源里,實(shí)際項(xiàng)目是通過 API 從服務(wù)器下載的配置文件,以實(shí)現(xiàn)實(shí)時(shí)更新埋點(diǎn)配置。

測(cè)試 json 文件:

  1.     "Gesture":{ 
  2.         "RootViewController/gestureclicked:":{ 
  3.             "userDefined": { 
  4.                 "action""click"
  5.                 "pageid""1234"
  6.                 "pageName""首頁"
  7.                 "eventName":"點(diǎn)擊手勢(shì)" 
  8.             }, 
  9.             "eventParam":{ 
  10.                 "spm":"a-b-c-spm"
  11.                 "pageName":""
  12.                 "tips":"" 
  13.             } 
  14.         } 
  15.     }, 
  16.     "ViewController":{ 
  17.         "RootViewController/viewWillAppear":{ 
  18.             "userDefined": { 
  19.                 "action""show"
  20.                 "pageid""1234"
  21.                 "pageName""首頁"
  22.                 "eventName":"首頁展示" 
  23.             }, 
  24.             "eventParam":{ 
  25.                 "spm":""
  26.                 "pageName":""
  27.                 "tips":"" 
  28.             } 
  29.         }, 
  30.         "SecondViewController/viewWillAppear":{ 
  31.             "userDefined": { 
  32.                 "action""show"
  33.                 "pageid""1235"
  34.                 "pageName""靈感頁"
  35.                 "eventName":"靈感頁展示" 
  36.             }, 
  37.             "eventParam":{ 
  38.                 "spm":""
  39.                 "pageName":""
  40.                 "tips":"" 
  41.             } 
  42.         } 
  43.     }, 
  44.     "CollectionView":{ 
  45.         "ThirdViewController/0":{ 
  46.             "viewcontroller":true
  47.             "userDefined": { 
  48.                 "action""click"
  49.                 "pageid""12345"
  50.                 "pageName""靈感頁"
  51.                 "eventName":"點(diǎn)擊collectionview" 
  52.             }, 
  53.             "eventParam":{ 
  54.                 "spm":"a-b-c-spm"
  55.                 "pageName":""
  56.                 "tips":"" 
  57.             } 
  58.         } 
  59.     }, 
  60.     "TableView":{ 
  61.         "SecondViewController/0":{ 
  62.             "viewcontroller":true
  63.             "userDefined": { 
  64.                 "action""click"
  65.                 "pageid""12345"
  66.                 "pageName""靈感頁"
  67.                 "eventName":"點(diǎn)擊tableview" 
  68.             }, 
  69.             "eventParam":{ 
  70.                 "spm":"a-b-c-spm"
  71.                 "pageName":""
  72.                 "tips":"" 
  73.             } 
  74.         } 
  75.     }, 
  76.     "Action":{ 
  77.         "RootViewController/testButtonClick:/0":{ 
  78.             "userDefined": { 
  79.                 "action""click"
  80.                 "pageid""1234"
  81.                 "pageName""首頁"
  82.                 "eventName":"點(diǎn)擊測(cè)試按鈕" 
  83.             }, 
  84.             "eventParam":{ 
  85.                 "spm":"a-b-c-spm"
  86.                 "pageName":""
  87.                 "tips":"" 
  88.             } 
  89.         }, 
  90.         "SecondViewController/UIView/UITableView/TableViewCell/testButtonClick:/0":{ 
  91.             "userDefined": { 
  92.                 "action""click"
  93.                 "pageid""1234"
  94.                 "pageName""靈感"
  95.                 "eventName":"cell里的點(diǎn)擊測(cè)試按鈕" 
  96.             }, 
  97.             "eventParam":{ 
  98.                 "spm":"a-b-c-spm"
  99.                 "pageName":""
  100.                 "tips":"" 
  101.             } 
  102.         } 
  103.     } 

總結(jié)

 

使用運(yùn)行時(shí)方法的替換實(shí)現(xiàn)了無侵入埋點(diǎn),但仍存在很多問題,比如唯一標(biāo)識(shí)難以維護(hù)、準(zhǔn)確性有待驗(yàn)證。目前的方式只能實(shí)現(xiàn)頁面進(jìn)、出以及點(diǎn)擊事件的埋點(diǎn)統(tǒng)計(jì),涉及到具體業(yè)務(wù)的埋點(diǎn)統(tǒng)計(jì),比如開機(jī)啟動(dòng)、需要上報(bào)參數(shù)信息等類型的埋點(diǎn)還是要依賴代碼埋點(diǎn)。所以無侵入埋點(diǎn)方案還有很大優(yōu)化空間。

 

責(zé)任編輯:武曉燕 來源: 網(wǎng)羅開發(fā)
相關(guān)推薦

2020-04-29 16:24:55

開發(fā)iOS技術(shù)

2016-12-12 13:42:54

數(shù)據(jù)分析大數(shù)據(jù)埋點(diǎn)

2017-12-28 14:54:04

Android代碼埋點(diǎn)全埋點(diǎn)

2024-09-06 07:55:42

2023-12-13 18:46:50

FlutterAOP業(yè)務(wù)層

2021-02-19 07:59:21

數(shù)據(jù)埋點(diǎn)數(shù)據(jù)分析大數(shù)據(jù)

2023-01-10 09:08:53

埋點(diǎn)數(shù)據(jù)數(shù)據(jù)處理

2023-04-19 09:05:44

2021-08-31 19:14:38

技術(shù)埋點(diǎn)運(yùn)營

2023-12-07 07:23:39

APIsSDKs

2014-06-10 13:44:58

iOSUIImage知識(shí)點(diǎn)

2022-10-14 08:47:42

埋點(diǎn)統(tǒng)計(jì)優(yōu)化

2022-11-01 18:21:14

數(shù)據(jù)埋點(diǎn)SDK

2024-03-06 19:57:56

探索商家可視化

2024-08-29 14:44:01

質(zhì)檢埋點(diǎn)

2023-09-05 07:28:02

Java自動(dòng)埋點(diǎn)

2020-04-10 10:36:20

網(wǎng)絡(luò)通信框架

2017-04-11 15:34:41

機(jī)票前臺(tái)埋點(diǎn)

2025-01-22 14:00:12

2013-06-14 11:21:43

iOS開發(fā)移動(dòng)開發(fā)畫圖
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)