iOS 全埋点-应用程序退出和启动(1)

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

写在前面

本章节主要是介绍埋点的分类,以及应用程序退出和启动埋点分析,更多内容以章节内容形式输出。

一、埋点分类

目前,业界主流的埋点方式主要有如下三 种。

1、代码埋点

应用程序集成埋点SDK后,在启动时初始化埋点SDK,然后在某个事件发生的时候调用埋点SDK提供的方法来触发事件。

1.1、优点

  • 可以精准控制埋点的位置
  • 可以方便、灵活地自定义事件和属性
  • 可以采集更丰富的和业务相关的数据
  • 可以满足更加精细化的分析需求

1.2、缺点

  • 前期埋点的成本相对较高
  • 若分析需求或事件发生变化,则需要修改应用程序埋点并发版

2、全埋点

全埋点也叫无埋点、无码埋点、无痕埋点、自动埋点,指无须应用程序开发工程师写代码或者只写少量的代码,即可预先自动收集用户的所有或者绝大部分的行为数据,然后根据实际的业务分析需求从中筛选出所需的数据并进行分析。

2.1、全埋点可以采集的事件

  • 应用程序的启动事件$AppStart

· 冷启动:应用程序被系统终止后,在这种状 态下启动的应用程序。
· 热启动:应用程序没有被系统终止,仍在后 台运行,在这种状态下启动的应用程序

  • 应用程序退出事件$AppEnd

·双击Home键切换到其他应用程序。
·单击Home键让当前应用程序进入后台。
·双击Home键并上滑,强杀当前应用程序。
·当前应用程序发生崩溃导致应用程序退出。

  • 页面浏览事件$AppViewScreen

应用程序内的页 面浏览事件,对于iOS应用程序来说,就是指切换 不同的UIViewController

  • 控件单击事件$AppClick

控件点击事件,比如 点击UIButtonUITableView等。

  • 应用程序崩溃事件

2.2、优点

  • 前期埋点成本相对低
  • 若分析需求或事件设计发生变化,无须应用程序修改埋点并发版
  • 可以有效地解决历史数据回朔问题

2.3、 缺点

  • 很难做到全面的覆盖
  • 无法自动采集和业务相关的数据
  • 无法满足更精细化的分析数据
  • 各种兼容性能方面问题

3、可视化埋点

可视化埋点也叫圈选,是指通过可视化的方式进行埋点

3.1、可视化埋点一般有两种应用场景

  • 默认情况下,不进行任何埋点,然后通过可视化的方式指定给哪些控件进行埋点(指定埋点)
  • 默认情况下,全部进行埋点,然后通过可视化的方式指定不给哪些控件进行埋点(排除埋点)。

3.2、优缺点

可视化埋点的优点和缺点,整体上与全埋点的优点和缺点类似。

二、全埋点

正常情况下,iOS应用程序主要有5种常见的状态。

  • (1)Not running。非运行状态,指应用程序 还没有被启动,或者已被系统终止。
  • (2)Inactive。前台非活动状态,指应用程序 即将进入前台状态,但当前未接收到任何事件 (可能正在执行其他代码)。应用程序通常只在 转换到其他状态时才会短暂地进入该状态。
  • (3)Active。前台活跃状态,指应用程序正 在前台运行,可接收事件并进行处理。这也是一 个iOS应用程序处于前台的正常模式。
  • (4)Background。进入后台状态,指应用程 序进入后台并可执行代码。大多数应用程序在被 挂起前都会短暂地进入该状态。
  • (5)Suspended。挂起状态,指应用程序进 入后台但没有执行任何代码,系统会自动地将应 用程序转移到该状态,并且在执行该操作前不会 通知应用程序。挂起时,应用程序会保留在内存 中,但不执行任何代码。当系统出现内存不足情 况时,系统可能会在未通知应用程序的情况下清 除被挂起的应用程序,为前台应用程序尽可能腾 出更多的运行资源。

在应用程序的状态转换过程中,系统会回调 实现UIApplicationDelegate协议类的一些方法(如 在Demo中,Xcode默认创建AppDelegate类),并发送相应的本地通知(系统会先回调相应的方法,待回调方法执行后,再发送相应的通知)。 回调方法和本地通知的对应关系,如下图所示:

系统本地通知.png 这里我创建一个SDK工具类(SensorsSDK),方便后续集成。 新建一个SensorsSDK工具SDK,如下图所示:

采集SDK.png

基本预置属性

一般情况下,用户触发的任何事件都携带一些最基本的信息,比如操 作系统类型、操作系统版本号、运营商信息、应用程序版本号、生产厂商 等,这些信息都可以由埋点SDK自动采集。我们把这些默认由埋点SDK自 动采集的事件基本信息(属性)称为预置属性。 当前你也可以定义需要采集的其他信息。

我们可以在SensorsAnalyticsSDK类初始化时获取这些预置属性,然后 在触发事件时,将这些预置属性添加到每一个事件中。 首先,在SensorsAnalyticsSDK.m文件中新增一个 NSDictionary<NSString *,**id**>类型的属性automaticProperties,用于保存事件 的预置属性。

说明: $为区分系统定义的事件标识前缀

预置属性 说明
$os 操作系统类型
$lib SDK类型
$manufacturer 设置制造商
$lib_version SDK版本号
$os_version 操作系统版本号
$model 手机型号
$app_version 应用程序版本号

注意点

  • 下拉通知栏时,系统会发送 UIApplicationWillResignActiveNotification本地通知;上滑 通知栏时,系统会发送 UIApplicationDidBecomeActiveNotification本地通知。
  • 上滑控制中心时,系统会发送 UIApplicationWillResignActiveNotification本地通知;下拉控制中心时,系统会发送 UIApplicationDidBecomeActiveNotification本地通知。
  • 双击Home键进入切换应用程序页面时,系统会发送 UIApplicationWillResignActive-Notification本地通知;选择 当前应用程序,系统会发送UIApplicationDidBecome- ActiveNotification本地通知。

进入应用和退出应用埋点

示例代码如下:

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface SensorsAnalyticsSDK : NSObject

/**
 @abstract 获取SDK实例
 @return返回单例
 */
+ (SensorsAnalyticsSDK *)sharedInstance;
@end

@interface SensorsAnalyticsSDK (Track)
/**
 @abstract 调用Track接口,触发事件
 @discussion properties是一个NSDictionary(字典)。 其中,key是属性的名称,必须是NSString类型;value则是属性的内容
 @param eventName 事件名称
 @param properties 事件属性
 */
- (void)track:(NSString *)eventName properties:(nullable NSDictionary<NSString *, id> *)properties;
@end
复制代码
#import "SensorsAnalyticsSDK.h"
//获取手机设备型号
#import <sys/sysctl.h>
//SDK系统版本号
static NSString *const SensorsAnalyticsVersion = @"1.0.0";
@interface SensorsAnalyticsSDK ()
/// 由SDK默认自动采集的事件属性即预置属性
@property (nonatomic, strong) NSDictionary<NSString *, id> *automaticProperties;
/// 标记应用程序是否已收到UIApplicationWillResignActiveNotification本地通知
@property (nonatomic, assign) BOOL applicationWillResignActive;
///是否为被动启动(后台应用程序刷新)
@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];     
        //设置是否位被动启动标记
        _launchedPassively = UIApplication.sharedApplication.backgroundTimeRemaining != UIApplicationBackgroundFetchIntervalNever;  
        //添加应用程序监听
        [self setupListeners];
    }
    return self;
}

#pragma mark- Properties
-(NSDictionary<NSString *,id> *)collectAutomaticProperties {  
    NSMutableDictionary *properties = [NSMutableDictionary dictionary];
    //操作系统
    properties[@"$os"] = @"iOS";
    //SDK类型
    properties[@"$lib"] = @"iOS";
    //设置制造商
    properties[@"$manufacturer"] = @"Apple";
    //SDK版本号
    properties[@"$lib_version"] = SensorsAnalyticsVersion;
    //操作系统版本号
    properties[@"$os_version"] = UIDevice.currentDevice.systemVersion;
    //手机型号
    properties[@"$model"] = [self deviceModel];
    //应用程序版本号
    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 - 监听
-(void)setupListeners {   
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
   
   //监听App进入后台
    [center addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
   
   //监听App进入前台并处于活跃状态
    [center addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
    
    //监听UIApplicationWillResignActiveNotification本地通知
    [center addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
    
    //注册监听UIApplicationDidFinishLaunchingNotification本地通知
    [center addObserver:self selector:@selector(applicationDidFinishLaunching:) name:UIApplicationDidFinishLaunchingNotification object:nil];
}

-(void)applicationDidEnterBackground:(NSNotification *)notification {
    NSLog(@"App进入后台");
    //还原标记位
    self.applicationWillResignActive = NO;
    //触发$AppEnd事件
    [self track:@"$AppEnd" properties:nil];
}

-(void)applicationDidBecomeActive:(NSNotification *)notification {
    NSLog(@"App进入前台");
    //还原标记位
    if (self.applicationWillResignActive) {
        self.applicationWillResignActive = NO;
        return;
    } 
    //将启动标记设为NO.正常记录事件
    self.launchedPassively = NO; 
    //触发$AppStart事件
    [self track:@"$AppStart" properties:nil];
}

-(void)applicationWillResignActive:(NSNotification *)notification {
    
    NSLog(@"App即将进入前台");
    self.applicationWillResignActive = YES;
}

-(void)applicationDidFinishLaunching:(NSNotification *)notification {
    
    NSLog(@"App启动啦");
    //当应用程序后台运行时,触发被动启动事件
    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];
    // 设置事件名称
    event[@"event"] = eventName;
    // 设置事件发生的时间戳,单位为毫秒
    event[@"time"] = [NSNumber numberWithLong:NSDate.date.timeIntervalSince1970 * 1000];
    NSMutableDictionary *eventProperties = [NSMutableDictionary dictionary];
    // 添加预置属性
    [eventProperties addEntriesFromDictionary:self.automaticProperties];
    // 添加自定义属性
    [eventProperties addEntriesFromDictionary:properties];
    //判断是否位被动启动状态
    if(self.isLaunchedPassively) {
        //添加应用程序状态属性
        eventProperties[@"$app_state"] = @"background";
    }
     
    // 设置事件属性
    event[@"properties"] = eventProperties;
   
    [self printEvent:event];
}

@end

复制代码

猜你喜欢

转载自juejin.im/post/7018108066507800612