iOS 全埋點-應(yīng)用程序退出和啟動(1)

傳送門:

一、埋點分類

目前,業(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容