傳送門:
一、埋點分類
目前,業(yè)界主流的埋點方式主要有如下三 種。
1、代碼埋點
應(yīng)用程序集成埋點SDK后,在啟動時初始化埋點SDK,然后在某個事件發(fā)生的時候調(diào)用埋點SDK提供的方法來觸發(fā)事件。
1.1、優(yōu)點
- 可以精準控制埋點的位置
- 可以方便、靈活地自定義事件和屬性
- 可以采集更豐富的和業(yè)務(wù)相關(guān)的數(shù)據(jù)
- 可以滿足更加精細化的分析需求
1.2、缺點
- 前期埋點的成本相對較高
- 若分析需求或事件發(fā)生變化,則需要修改應(yīng)用程序埋點并發(fā)版
2、全埋點
全埋點也叫無埋點、無碼埋點、無痕埋點、自動埋點,指無須應(yīng)用程序開發(fā)工程師寫代碼或者只寫少量的代碼,即可預(yù)先自動收集用戶的所有或者絕大部分的行為數(shù)據(jù),然后根據(jù)實際的業(yè)務(wù)分析需求從中篩選出所需的數(shù)據(jù)并進行分析。
2.1、全埋點可以采集的事件
-
應(yīng)用程序的啟動事件(
$AppStart)
· 冷啟動:應(yīng)用程序被系統(tǒng)終止后,在這種狀 態(tài)下啟動的應(yīng)用程序。
· 熱啟動:應(yīng)用程序沒有被系統(tǒng)終止,仍在后 臺運行,在這種狀態(tài)下啟動的應(yīng)用程序 -
應(yīng)用程序退出事件(
$AppEnd)
·雙擊Home鍵切換到其他應(yīng)用程序。
·單擊Home鍵讓當前應(yīng)用程序進入后臺。
·雙擊Home鍵并上滑,強殺當前應(yīng)用程序。
·當前應(yīng)用程序發(fā)生崩潰導(dǎo)致應(yīng)用程序退出。 -
頁面瀏覽事件(
$AppViewScreen)
應(yīng)用程序內(nèi)的頁 面瀏覽事件,對于iOS應(yīng)用程序來說,就是指切換 不同的UIViewController。 -
控件單擊事件(
$AppClick)
控件點擊事件,比如 點擊UIButton、UITableView等。 - 應(yīng)用程序崩潰事件
2.2、優(yōu)點
- 前期埋點成本相對低
- 若分析需求或事件設(shè)計發(fā)生變化,無須應(yīng)用程序修改埋點并發(fā)版
- 可以有效地解決歷史數(shù)據(jù)回朔問題
2.3、 缺點
- 很難做到全面的覆蓋
- 無法自動采集和業(yè)務(wù)相關(guān)的數(shù)據(jù)
- 無法滿足更精細化的分析數(shù)據(jù)
- 各種兼容性能方面問題
3、可視化埋點
可視化埋點也叫圈選,是指通過可視化的方式進行埋點
3.1、可視化埋點一般有兩種應(yīng)用場景
- 默認情況下,不進行任何埋點,然后通過可視化的方式指定給哪些控件進行埋點(指定埋點)
- 默認情況下,全部進行埋點,然后通過可視化的方式指定不給哪些控件進行埋點(排除埋點)。
3.2、優(yōu)缺點
可視化埋點的優(yōu)點和缺點,整體上與全埋點的優(yōu)點和缺點類似。
二、全埋點
正常情況下,iOS應(yīng)用程序主要有5種常見的狀態(tài)。
- (1
)Not running。非運行狀態(tài),指應(yīng)用程序 還沒有被啟動,或者已被系統(tǒng)終止。 - (2)
Inactive。前臺非活動狀態(tài),指應(yīng)用程序 即將進入前臺狀態(tài),但當前未接收到任何事件 (可能正在執(zhí)行其他代碼)。應(yīng)用程序通常只在 轉(zhuǎn)換到其他狀態(tài)時才會短暫地進入該狀態(tài)。 - (3)
Active。前臺活躍狀態(tài),指應(yīng)用程序正 在前臺運行,可接收事件并進行處理。這也是一 個iOS應(yīng)用程序處于前臺的正常模式。 - (4)
Background。進入后臺狀態(tài),指應(yīng)用程 序進入后臺并可執(zhí)行代碼。大多數(shù)應(yīng)用程序在被 掛起前都會短暫地進入該狀態(tài)。 - (5)
Suspended。掛起狀態(tài),指應(yīng)用程序進 入后臺但沒有執(zhí)行任何代碼,系統(tǒng)會自動地將應(yīng) 用程序轉(zhuǎn)移到該狀態(tài),并且在執(zhí)行該操作前不會 通知應(yīng)用程序。掛起時,應(yīng)用程序會保留在內(nèi)存 中,但不執(zhí)行任何代碼。當系統(tǒng)出現(xiàn)內(nèi)存不足情 況時,系統(tǒng)可能會在未通知應(yīng)用程序的情況下清 除被掛起的應(yīng)用程序,為前臺應(yīng)用程序盡可能騰 出更多的運行資源。
在應(yīng)用程序的狀態(tài)轉(zhuǎn)換過程中,系統(tǒng)會回調(diào) 實現(xiàn)UIApplicationDelegate協(xié)議類的一些方法(如 在Demo中,Xcode默認創(chuàng)建AppDelegate類),并發(fā)送相應(yīng)的本地通知(系統(tǒng)會先回調(diào)相應(yīng)的方法,待回調(diào)方法執(zhí)行后,再發(fā)送相應(yīng)的通知)。 回調(diào)方法和本地通知的對應(yīng)關(guān)系,如下圖所示:

這里我創(chuàng)建一個SDK工具類(SensorsSDK),方便后續(xù)集成。
新建一個SensorsSDK工具SDK,如下圖所示:

基本預(yù)置屬性
一般情況下,用戶觸發(fā)的任何事件都攜帶一些最基本的信息,比如操 作系統(tǒng)類型、操作系統(tǒng)版本號、運營商信息、應(yīng)用程序版本號、生產(chǎn)廠商 等,這些信息都可以由埋點SDK自動采集。我們把這些默認由埋點SDK自 動采集的事件基本信息(屬性)稱為預(yù)置屬性。 當前你也可以定義需要采集的其他信息。
我們可以在SensorsAnalyticsSDK類初始化時獲取這些預(yù)置屬性,然后 在觸發(fā)事件時,將這些預(yù)置屬性添加到每一個事件中。 首先,在SensorsAnalyticsSDK.m文件中新增一個 NSDictionary<NSString *,**id**>類型的屬性automaticProperties,用于保存事件 的預(yù)置屬性。
說明: $為區(qū)分系統(tǒng)定義的事件標識前綴
| 預(yù)置屬性 | 說明 |
|---|---|
| $os | 操作系統(tǒng)類型 |
| $lib | SDK類型 |
| $manufacturer | 設(shè)置制造商 |
| $lib_version | SDK版本號 |
| $os_version | 操作系統(tǒng)版本號 |
| $model | 手機型號 |
| $app_version | 應(yīng)用程序版本號 |
注意點
- 下拉通知欄時,系統(tǒng)會發(fā)送
UIApplicationWillResignActiveNotification本地通知;上滑 通知欄時,系統(tǒng)會發(fā)送UIApplicationDidBecomeActiveNotification本地通知。 - 上滑控制中心時,系統(tǒng)會發(fā)送
UIApplicationWillResignActiveNotification本地通知;下拉控制中心時,系統(tǒng)會發(fā)送UIApplicationDidBecomeActiveNotification本地通知。 - 雙擊Home鍵進入切換應(yīng)用程序頁面時,系統(tǒng)會發(fā)送
UIApplicationWillResignActive-Notification本地通知;選擇 當前應(yīng)用程序,系統(tǒng)會發(fā)送UIApplicationDidBecome- ActiveNotification本地通知。
示例代碼如下:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface SensorsAnalyticsSDK : NSObject
/**
@abstract 獲取SDK實例
@return返回單例
*/
+ (SensorsAnalyticsSDK *)sharedInstance;
@end
@interface SensorsAnalyticsSDK (Track)
/**
@abstract 調(diào)用Track接口,觸發(fā)事件
@discussion properties是一個NSDictionary(字典)。 其中,key是屬性的名稱,必須是NSString類型;value則是屬性的內(nèi)容
@param eventName 事件名稱
@param properties 事件屬性
*/
- (void)track:(NSString *)eventName properties:(nullable NSDictionary<NSString *, id> *)properties;
@end
#import "SensorsAnalyticsSDK.h"
//獲取手機設(shè)備型號
#import <sys/sysctl.h>
//SDK系統(tǒng)版本號
static NSString *const SensorsAnalyticsVersion = @"1.0.0";
@interface SensorsAnalyticsSDK ()
/// 由SDK默認自動采集的事件屬性即預(yù)置屬性
@property (nonatomic, strong) NSDictionary<NSString *, id> *automaticProperties;
/// 標記應(yīng)用程序是否已收到UIApplicationWillResignActiveNotification本地通知
@property (nonatomic, assign) BOOL applicationWillResignActive;
///是否為被動啟動(后臺應(yīng)用程序刷新)
@property (nonatomic, getter=isLaunchedPassively) BOOL launchedPassively;
@end
@implementation SensorsAnalyticsSDK
- (void)dealloc
{
[[NSNotificationCenter defaultCenter]removeObserver:self];
}
+ (SensorsAnalyticsSDK *)sharedInstance {
static SensorsAnalyticsSDK *sdk = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sdk = [[SensorsAnalyticsSDK alloc] init];
});
return sdk;
}
- (instancetype)init {
self = [super init];
if (self) {
_automaticProperties = [self collectAutomaticProperties];
//設(shè)置是否位被動啟動標記
_launchedPassively = UIApplication.sharedApplication.backgroundTimeRemaining != UIApplicationBackgroundFetchIntervalNever;
//添加應(yīng)用程序監(jiān)聽
[self setupListeners];
}
return self;
}
#pragma mark- Properties
-(NSDictionary<NSString *,id> *)collectAutomaticProperties {
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
//操作系統(tǒng)
properties[@"$os"] = @"iOS";
//SDK類型
properties[@"$lib"] = @"iOS";
//設(shè)置制造商
properties[@"$manufacturer"] = @"Apple";
//SDK版本號
properties[@"$lib_version"] = SensorsAnalyticsVersion;
//操作系統(tǒng)版本號
properties[@"$os_version"] = UIDevice.currentDevice.systemVersion;
//手機型號
properties[@"$model"] = [self deviceModel];
//應(yīng)用程序版本號
properties[@"$app_version"] = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"];
return [properties copy];
}
//獲取手機的型號
-(NSString *) deviceModel {
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char answer[size];
sysctlbyname("hw.machine", answer, &size, NULL, 0);
NSString *results = @(answer);
return results;
}
-(void)printEvent:(NSDictionary *)event {
#if DEBUG
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:event options:NSJSONWritingSortedKeys error:&error];
if (error) {
return NSLog(@"Json Serialzed error:%@",error);
}
NSString *json = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"[event]:%@",json);
#endif
}
#pragma mark - 監(jiān)聽
-(void)setupListeners {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
//監(jiān)聽App進入后臺
[center addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
//監(jiān)聽App進入前臺并處于活躍狀態(tài)
[center addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
//監(jiān)聽UIApplicationWillResignActiveNotification本地通知
[center addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
//注冊監(jiān)聽UIApplicationDidFinishLaunchingNotification本地通知
[center addObserver:self selector:@selector(applicationDidFinishLaunching:) name:UIApplicationDidFinishLaunchingNotification object:nil];
}
-(void)applicationDidEnterBackground:(NSNotification *)notification {
NSLog(@"App進入后臺");
//還原標記位
self.applicationWillResignActive = NO;
//觸發(fā)$AppEnd事件
[self track:@"$AppEnd" properties:nil];
}
-(void)applicationDidBecomeActive:(NSNotification *)notification {
NSLog(@"App進入前臺");
//還原標記位
if (self.applicationWillResignActive) {
self.applicationWillResignActive = NO;
return;
}
//將啟動標記設(shè)為NO.正常記錄事件
self.launchedPassively = NO;
//觸發(fā)$AppStart事件
[self track:@"$AppStart" properties:nil];
}
-(void)applicationWillResignActive:(NSNotification *)notification {
NSLog(@"App即將進入前臺");
self.applicationWillResignActive = YES;
}
-(void)applicationDidFinishLaunching:(NSNotification *)notification {
NSLog(@"App啟動啦");
//當應(yīng)用程序后臺運行時,觸發(fā)被動啟動事件
if (self.isLaunchedPassively) {
[self track:@"$AppStartPassively" properties:nil];
}
}
@end
@implementation SensorsAnalyticsSDK (Track)
- (void)track:(NSString *)eventName properties:(NSDictionary<NSString *,id> *)properties {
NSMutableDictionary *event = [NSMutableDictionary dictionary];
// 設(shè)置事件名稱
event[@"event"] = eventName;
// 設(shè)置事件發(fā)生的時間戳,單位為毫秒
event[@"time"] = [NSNumber numberWithLong:NSDate.date.timeIntervalSince1970 * 1000];
NSMutableDictionary *eventProperties = [NSMutableDictionary dictionary];
// 添加預(yù)置屬性
[eventProperties addEntriesFromDictionary:self.automaticProperties];
// 添加自定義屬性
[eventProperties addEntriesFromDictionary:properties];
//判斷是否位被動啟動狀態(tài)
if(self.isLaunchedPassively) {
//添加應(yīng)用程序狀態(tài)屬性
eventProperties[@"$app_state"] = @"background";
}
// 設(shè)置事件屬性
event[@"properties"] = eventProperties;
[self printEvent:event];
}
@end