美文网首页
iOS 全埋点-应用程序退出和启动(1)

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

作者: smile_frank | 来源:发表于2021-10-12 17:35 被阅读0次

    传送门:

    一、埋点分类

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

    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类),并发送相应的本地通知(系统会先回调相应的方法,待回调方法执行后,再发送相应的通知)。 回调方法和本地通知的对应关系,如下图所示:

    这里我创建一个SDK工具类(SensorsSDK),方便后续集成。
    新建一个SensorsSDK工具SDK,如下图所示:

    基本预置属性

    一般情况下,用户触发的任何事件都携带一些最基本的信息,比如操 作系统类型、操作系统版本号、运营商信息、应用程序版本号、生产厂商 等,这些信息都可以由埋点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
    
    

    相关文章

      网友评论

          本文标题:iOS 全埋点-应用程序退出和启动(1)

          本文链接:https://www.haomeiwen.com/subject/ixqjoltx.html