iOS全埋点解决方案-数据同步

打印 上一主题 下一主题

主题 871|帖子 871|积分 2613


前言

​        将本地存储的事件数据同步到服务器,然后经过服务端的存储、抽取、分析和展示,充分发挥数据真正的价值。
一、数据同步

第一步:在 SensorsSDK 项目中,新增 SensorsAnalyticsNetwork 工具类,并新增 serverURL 用于保存服务器 URL 地址
  1. #import <Foundation/Foundation.h>
  2. NS_ASSUME_NONNULL_BEGIN
  3. @interface SensorsAnalyticsNetwork : NSObject
  4. /// 数据上报的服务器
  5. @property (nonatomic, strong) NSURL *serverURL;
  6. @end
  7. NS_ASSUME_NONNULL_END
复制代码
第二步:新增 - initWithServerURL: 初始化方法,并禁用 - init 初始化方法
  1. #import <Foundation/Foundation.h>
  2. NS_ASSUME_NONNULL_BEGIN
  3. @interface SensorsAnalyticsNetwork : NSObject
  4. /// 数据上报的服务器
  5. @property (nonatomic, strong) NSURL *serverURL;
  6. /// 禁止使用 - init 方法进行初始化
  7. - (instancetype)init NS_UNAVAILABLE;
  8. /// 指定初始化方法
  9. /// @param serverURL 服务器 URL 地址
  10. - (instancetype)initWithServerURL:(NSURL *)serverURL NS_DESIGNATED_INITIALIZER;
  11. @end
  12. NS_ASSUME_NONNULL_END
复制代码
第三步:在 SensorsAnalyticsNetwork.m 中新增 NSURLSession 类型的 session 属性,并在  - initWithServerURL:方法中进行初始化
  1. @interface SensorsAnalyticsNetwork() <NSURLSessionDelegate>
  2. @property (nonatomic, strong) NSURLSession *session;
  3. @end
  4. @implementation SensorsAnalyticsNetwork
  5. - (instancetype)initWithServerURL:(NSURL *)serverURL {
  6.     self = [super init];
  7.     if (self) {
  8.         _serverURL = serverURL;
  9.         
  10.         // 创建默认的 session 配置对象
  11.         NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
  12.         // 设置当个逐级连接数为 5
  13.         configuration.HTTPMaximumConnectionsPerHost = 5;
  14.         // 设置请求的超时事件
  15.         configuration.timeoutIntervalForRequest = 30;
  16.         // 容许使用蜂窝网络连接
  17.         configuration.allowsCellularAccess = YES;
  18.         
  19.         // 创建一个网络请求回调和完成操作的线程池
  20.         NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  21.         // 设置同步运行的最大操作数为 1, 即个操作FIFO
  22.         queue.maxConcurrentOperationCount = 1;
  23.         // 通过配置对象创建一个 session 对象
  24.         _session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:queue];
  25.     }
  26.    
  27.     return self;
  28. }
复制代码
第四步:新增 - buildJSONStringWithEvents:方法,将事件数字转出字符串
  1. /// 将事件数组转换成字符串
  2. - (NSString *)buildJSONStringWithEvents:(NSArray<NSString *> *)events {
  3.     return [NSString stringWithFormat:@"[\n%@\n]", [events componentsJoinedByString:@".\n"]];
  4. }
复制代码
第五步:新增 - buildRequestWithJSONString:方法,用于根据 serverURL 和事件字符串来创建 NSURLRequest 请求
  1. - (NSURLRequest *)buildRequestWithJSONString:(NSString *)json {
  2.     // 通过服务器 URL 地址创建请求
  3.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.serverURL];
  4.     // 设置请求的body
  5.     request.HTTPBody = [json dataUsingEncoding:NSUTF8StringEncoding];
  6.     // 请求方法
  7.     request.HTTPMethod = @"POST";
  8.    
  9.     return request;
  10. }
复制代码
第六步:新增 - flushEvents: 方法,用于同步数据
  1. /// 网络请求结束处理回调类型
  2. typedef void(^SAURLSessionTaskCompletionHandler) (NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error);
  3. - (BOOL)flushEvents:(NSArray<NSString *> *)events {
  4.     // 将事件数组组装成JSON字符串
  5.     NSString *jsonString = [self buildJSONStringWithEvents:events];
  6.     // 创建请求对象
  7.     NSURLRequest *request = [self buildRequestWithJSONString:jsonString];
  8.     // 数据上传结果
  9.     __block BOOL flushSuccess = NO;
  10.     // 使用 GCD 中信号量,实现线程锁
  11.     dispatch_semaphore_t flushSemaphore = dispatch_semaphore_create(0);
  12.     SAURLSessionTaskCompletionHandler handler = ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  13.         if (error) {
  14.             // 当前请求发送错误,打印信息错误
  15.             NSLog(@"Flush events error: %@", error);
  16.             dispatch_semaphore_signal(flushSemaphore);
  17.             return;
  18.         }
  19.         
  20.         // 获取请求结束返回的状态码
  21.         NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
  22.         // 当状态码为 2xx 时,表示事件发送成功
  23.         if (statusCode >= 200 && statusCode < 300) {
  24.             // 打印上传成功的数据
  25.             NSLog(@"Flush events success: %@", jsonString);
  26.             // 数据上报成功
  27.             flushSuccess = YES;
  28.         } else {
  29.             // 事件信息发送失败
  30.             NSString *desc = [NSString stringWithFormat:@"Flush events error, statusCode: %d, events: %@", (int)statusCode, jsonString];
  31.             NSLog(@"Flush events error: %@", desc);
  32.         }
  33.         dispatch_semaphore_signal(flushSemaphore);
  34.     };
  35.    
  36.     // 通过 request 创建请求任务
  37.     NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:handler];
  38.     // 执行任务
  39.     [task resume];
  40.     // 等待请求完成
  41.     dispatch_semaphore_wait(flushSemaphore, DISPATCH_TIME_FOREVER);
  42.     // 返回数据上传结果
  43.     return flushSuccess;
  44. }
复制代码
第七步:在 SensorsAnalyticsSDK.m 文件中新增 SensorsAnalyticsNetwork 类型的 network 对象,并在 - init 方法中进行初始化
  1. #import "SensorsAnalyticsNetwork.h"
  2. /// 发送网络请求对象
  3. @property (nonatomic, strong) SensorsAnalyticsNetwork *network;
  4. - (instancetype)initWithServerURL:(NSString *)urlString {
  5.     self = [super init];
  6.     if (self) {
  7.         _automaticProperties = [self collectAutomaticProperties];
  8.         // 设置是否需是被动启动标记
  9.         _launchedPassively = UIApplication.sharedApplication.backgroundTimeRemaining != UIApplicationBackgroundFetchIntervalNever;
  10.         
  11.         _loginId = [[NSUserDefaults standardUserDefaults] objectForKey:SensorsAnalyticsLoginId];
  12.         
  13.         _trackTimer = [NSMutableDictionary dictionary];
  14.         
  15.         _enterBackgroundTrackTimerEvents = [NSMutableArray array];
  16.         
  17.         _fileStroe = [[SensorsAnalyticsFileStore alloc] init];
  18.         
  19.         _database = [[SensorsAnalyticsDatabase alloc] init];
  20.         
  21.         _network = [[SensorsAnalyticsNetwork alloc] initWithServerURL:[NSURL URLWithString:urlString]];
  22.         
  23.         // 添加应用程序状态监听
  24.         [self setupListeners];
  25.     }
  26.     return self;
  27. }
复制代码
第八步:暴露 - flush 数据上报的方法,并实现。
  1. /// 向服务器同步本地所有数据
  2. - (void)flush;
复制代码
  1. - (void)flush {
  2.     // 默认向服务端一次发送 50 条数据
  3.     [self flushByEventCount:SensorsAnalyticsDefalutFlushEventCount];
  4. }
  5. - (void)flushByEventCount:(NSUInteger) count {
  6.     // 获取本地数据
  7.     NSArray<NSString *> *events = [self.database selectEventsForCount:count];
  8.     // 当本地存储的数据为0或者上传 失败时,直接返回,退出递归调用
  9.     if (events.count == 0 || ![self.network flushEvents:events]) {
  10.         return;
  11.     }
  12.     // 当删除数据失败时,直接返回,退出递归调用,防止死循环
  13.     if ([self.database deleteEventsForCount:count]) {
  14.         return;
  15.     }
  16.     // 继续删除本地的其他数据
  17.     [self flushByEventCount:count];
  18. }
复制代码
第九步:测试验证
问题描述:serverURL 是在 -init 方法中硬编码的。我们需要支持 SDK 初始化的时候传入
第一步:在 SensorsAnalyticsSDK.h 文件中禁止直接使用 - init 方法初始化 SDK,并新增 + startWithServerURL:方法的声明
  1. @interface SensorsAnalyticsSDK : NSObject/// 设备 ID (匿名 ID)@property (nonatomic, copy) NSString *anonymousId;/// 事件开始发生的时间戳@property (nonatomic, strong) NSMutableDictionary *trackTimer;/// 获取 SDK 实例方法/// 返回单例对象+ (SensorsAnalyticsSDK *)sharedInstance;/// 用户登陆设置登陆 ID/// @param loginId 用户的登陆 ID- (void)login:(NSString *)loginId;/// 当前的时间+ (double)currentTime;/// 系统启动时间+ (double)systemUpTime;/// 向服务器同步本地所有数据
  2. - (void)flush;- (instancetype)init NS_UNAVAILABLE;/// 初始化 SDK/// @param urlString 接受数据的服务端URL+ (void)startWithServerURL:(NSString *) urlString;@end
复制代码
第二步:实现 - initWithServerURL:方法的初始化
  1. - (instancetype)initWithServerURL:(NSString *)urlString {
  2.     self = [super init];
  3.     if (self) {
  4.         _automaticProperties = [self collectAutomaticProperties];
  5.         // 设置是否需是被动启动标记
  6.         _launchedPassively = UIApplication.sharedApplication.backgroundTimeRemaining != UIApplicationBackgroundFetchIntervalNever;
  7.         
  8.         _loginId = [[NSUserDefaults standardUserDefaults] objectForKey:SensorsAnalyticsLoginId];
  9.         
  10.         _trackTimer = [NSMutableDictionary dictionary];
  11.         
  12.         _enterBackgroundTrackTimerEvents = [NSMutableArray array];
  13.         
  14.         _fileStroe = [[SensorsAnalyticsFileStore alloc] init];
  15.         
  16.         _database = [[SensorsAnalyticsDatabase alloc] init];
  17.         
  18.         _network = [[SensorsAnalyticsNetwork alloc] initWithServerURL:[NSURL URLWithString:urlString]];
  19.         
  20.         // 添加应用程序状态监听
  21.         [self setupListeners];
  22.     }
  23.     return self;
  24. }
复制代码
第三步:实现 + startWithServerURL: 类方法
  1. static SensorsAnalyticsSDK *sharedInstace = nil;
  2. + (void)startWithServerURL:(NSString *)urlString {
  3.     static dispatch_once_t onceToken;
  4.     dispatch_once(&onceToken, ^{
  5.         sharedInstace = [[SensorsAnalyticsSDK alloc] initWithServerURL:urlString];
  6.     });
  7. }
复制代码
第四步: 修改 + sharedInstance 方法
  1. + (SensorsAnalyticsSDK *)sharedInstance {
  2.     return sharedInstace;
  3. }
复制代码
第五步:测试验证
  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  2.     // Override point for customization after application launch.
  3.    
  4.     [SensorsAnalyticsSDK startWithServerURL:@"https//:www.baidu.com"];
  5.     [[SensorsAnalyticsSDK sharedInstance] track:@"MyFirstTrack" properties:@{@"testKey": @"testValue"}];
  6.    
  7.     return YES;
  8. }
复制代码
二、数据同步策略

​        上面的集成,需要手动触发。但是作为一个标准的数据采集 SDK,必须包含一些自动同步数据的策略,一方面是为了降低用户使用 SDK 的难度和成本,另一方面是为了确保数据的正确性、完整性和及时性。
2.1 基本原则


  • 策略一:客户端本地已经缓存的事件超过一定条数时同步数据
  • 策略二:客户端每隔一定的时间不同一次数据
  • 策略三:应用程序进入后天时尝试不同本地已缓存的所有数据
​        因为事件和事件之间是有先后顺序的,因此,在同步数据的时候,需要严格按照事件触发的时间吸纳后顺序同步数据。所以我们需要先优化 SDK 中 - flush 方法,并使其在队列中执行。
第一步:在 SensorsAnalyticsSDK.m 中新增 dispatch_queue_t 类型的属性 serialQueue,并在 -initWithServerURL:方法中进行初始化
  1. /// 队列
  2. @property (nonatomic, strong) dispatch_queue_t serialQueue;
  3. - (instancetype)initWithServerURL:(NSString *)urlString {
  4.     self = [super init];
  5.     if (self) {
  6.         _automaticProperties = [self collectAutomaticProperties];
  7.         // 设置是否需是被动启动标记
  8.         _launchedPassively = UIApplication.sharedApplication.backgroundTimeRemaining != UIApplicationBackgroundFetchIntervalNever;
  9.         
  10.         _loginId = [[NSUserDefaults standardUserDefaults] objectForKey:SensorsAnalyticsLoginId];
  11.         
  12.         _trackTimer = [NSMutableDictionary dictionary];
  13.         
  14.         _enterBackgroundTrackTimerEvents = [NSMutableArray array];
  15.         
  16.         _fileStroe = [[SensorsAnalyticsFileStore alloc] init];
  17.         
  18.         _database = [[SensorsAnalyticsDatabase alloc] init];
  19.         
  20.         _network = [[SensorsAnalyticsNetwork alloc] initWithServerURL:[NSURL URLWithString:urlString]];
  21.         
  22.         NSString *queueLabel = [NSString stringWithFormat:@"cn.sensorsdata.%@.%p", self.class, self];
  23.         _serialQueue = dispatch_queue_create(queueLabel.UTF8String, DISPATCH_QUEUE_SERIAL);
  24.         
  25.         // 添加应用程序状态监听
  26.         [self setupListeners];
  27.     }
  28.     return self;
  29. }
复制代码
第二步:修改 -flush 方法,并使其在队列中执行
  1. - (void)flush {
  2.     dispatch_async(self.serialQueue, ^{
  3.         // 默认向服务端一次发送 50 条数据
  4.         [self flushByEventCount:SensorsAnalyticsDefalutFlushEventCount];
  5.     });
  6. }
复制代码
第三步:优化 - track: properties: 方法,并使其在队列中执行
  1. - (void)track:(NSString *)eventName properties:(nullable NSDictionary<NSString *, id> *)properties {
  2.     NSMutableDictionary *event = [NSMutableDictionary dictionary];
  3.     // 设置事件 distinct_id 字段,用于唯一标识一个用户
  4.     event[@"distinct_id"] = self.loginId ?: self.anonymousId;
  5.     // 设置事件名称
  6.     event[@"event"] = eventName;
  7.     // 事件发生的时间戳,单位毫秒
  8.     event[@"time"] = [NSNumber numberWithLong:NSDate.date.timeIntervalSince1970 *1000];
  9.    
  10.     NSMutableDictionary *eventProperties = [NSMutableDictionary dictionary];
  11.     // 添加预置属性
  12.     [eventProperties addEntriesFromDictionary:self.automaticProperties];
  13.     // 添加自定义属性
  14.     [eventProperties addEntriesFromDictionary:properties];
  15.     // 判断是否是被动启动状态
  16.     if (self.isLaunchedPassively) {
  17.         eventProperties[@"$app_state"] = @"background";
  18.     }
  19.     // 设置事件属性
  20.     event[@"propeerties"] = eventProperties;
  21.    
  22.     dispatch_async(self.serialQueue, ^{
  23.         // 打印
  24.         [self printEvent:event];
  25.     //    [self.fileStroe saveEvent:event];
  26.         [self.database insertEvent:event];
  27.     });
  28. }
复制代码
2.2 策略一

​        客户端本地已经缓存的事件超过一定条数时同步数据。
​        实现策略:每次事件触发并入库后,检查一下已缓存的事件条数是否超过了定义的阈值,如果已达到,调用 - flush 方法同步数据。
第一步:添加属性 flushBulkSize ,表示允许本地缓存的事件最大条数
  1. /// 当本地缓存的事件达到最大条数时,上次数据(默认 100 条)
  2. @property (nonatomic, assign) NSInteger flushBulkSize;
复制代码
第二步:在 - initWithServerURL: 初始化中,初始化化 flushBulkSize 属性,默认值设置为 100 条
  1. - (instancetype)initWithServerURL:(NSString *)urlString {
  2.     self = [super init];
  3.     if (self) {
  4.         _automaticProperties = [self collectAutomaticProperties];
  5.         // 设置是否需是被动启动标记
  6.         _launchedPassively = UIApplication.sharedApplication.backgroundTimeRemaining != UIApplicationBackgroundFetchIntervalNever;
  7.         
  8.         _loginId = [[NSUserDefaults standardUserDefaults] objectForKey:SensorsAnalyticsLoginId];
  9.         
  10.         _trackTimer = [NSMutableDictionary dictionary];
  11.         
  12.         _enterBackgroundTrackTimerEvents = [NSMutableArray array];
  13.         
  14.         _fileStroe = [[SensorsAnalyticsFileStore alloc] init];
  15.         
  16.         _database = [[SensorsAnalyticsDatabase alloc] init];
  17.         
  18.         _network = [[SensorsAnalyticsNetwork alloc] initWithServerURL:[NSURL URLWithString:urlString]];
  19.         
  20.         NSString *queueLabel = [NSString stringWithFormat:@"cn.sensorsdata.%@.%p", self.class, self];
  21.         _serialQueue = dispatch_queue_create(queueLabel.UTF8String, DISPATCH_QUEUE_SERIAL);
  22.         
  23.         _flushBulkSize = 100;
  24.         
  25.         // 添加应用程序状态监听
  26.         [self setupListeners];
  27.     }
  28.     return self;
  29. }
复制代码
第三步:在 - track: properties: 方法,事件入库之后,判断本地缓存的事件条数是否大于 flushBulkSize ,入股大于,则触发数据同步
  1. - (void)track:(NSString *)eventName properties:(nullable NSDictionary<NSString *, id> *)properties {
  2.     NSMutableDictionary *event = [NSMutableDictionary dictionary];
  3.     // 设置事件 distinct_id 字段,用于唯一标识一个用户
  4.     event[@"distinct_id"] = self.loginId ?: self.anonymousId;
  5.     // 设置事件名称
  6.     event[@"event"] = eventName;
  7.     // 事件发生的时间戳,单位毫秒
  8.     event[@"time"] = [NSNumber numberWithLong:NSDate.date.timeIntervalSince1970 *1000];
  9.    
  10.     NSMutableDictionary *eventProperties = [NSMutableDictionary dictionary];
  11.     // 添加预置属性
  12.     [eventProperties addEntriesFromDictionary:self.automaticProperties];
  13.     // 添加自定义属性
  14.     [eventProperties addEntriesFromDictionary:properties];
  15.     // 判断是否是被动启动状态
  16.     if (self.isLaunchedPassively) {
  17.         eventProperties[@"$app_state"] = @"background";
  18.     }
  19.     // 设置事件属性
  20.     event[@"propeerties"] = eventProperties;
  21.    
  22.     dispatch_async(self.serialQueue, ^{
  23.         // 打印
  24.         [self printEvent:event];
  25.     //    [self.fileStroe saveEvent:event];
  26.         [self.database insertEvent:event];
  27.     });
  28.    
  29.     if (self.database.eventCount >= self.flushBulkSize) {
  30.         [self flush];
  31.     }
  32. }
复制代码
2.3 策略二

​        客户端每隔一定的时间同步一次数据,(比如默认 15 秒)
​        实现策略:开启一个定时器,每隔一定时间调用一次 -flush 方法。
第一步:添加 flushInterval 属性,两次数据发送的时间间隔,然后再 - initWithServerURL: 初始化方法中初始化
  1. /// 两次数据发送的时间间隔,单位为秒
  2. @property (nonatomic) NSUInteger flushInterval;
  3. - (instancetype)initWithServerURL:(NSString *)urlString {
  4.     self = [super init];
  5.     if (self) {
  6.         _automaticProperties = [self collectAutomaticProperties];
  7.         // 设置是否需是被动启动标记
  8.         _launchedPassively = UIApplication.sharedApplication.backgroundTimeRemaining != UIApplicationBackgroundFetchIntervalNever;
  9.         
  10.         _loginId = [[NSUserDefaults standardUserDefaults] objectForKey:SensorsAnalyticsLoginId];
  11.         
  12.         _trackTimer = [NSMutableDictionary dictionary];
  13.         
  14.         _enterBackgroundTrackTimerEvents = [NSMutableArray array];
  15.         
  16.         _fileStroe = [[SensorsAnalyticsFileStore alloc] init];
  17.         
  18.         _database = [[SensorsAnalyticsDatabase alloc] init];
  19.         
  20.         _network = [[SensorsAnalyticsNetwork alloc] initWithServerURL:[NSURL URLWithString:urlString]];
  21.         
  22.         NSString *queueLabel = [NSString stringWithFormat:@"cn.sensorsdata.%@.%p", self.class, self];
  23.         _serialQueue = dispatch_queue_create(queueLabel.UTF8String, DISPATCH_QUEUE_SERIAL);
  24.         
  25.         _flushBulkSize = 100;
  26.         
  27.         _flushInterval = 15;
  28.         
  29.         // 添加应用程序状态监听
  30.         [self setupListeners];
  31.         
  32.         [self startFlushTimer];
  33.     }
  34.     return self;
  35. }
复制代码
第二步:新增 flushTimer 属性,并实现 定时器方法
  1. /// 定时上次事件的定时器
  2. @property (nonatomic, strong) NSTimer *flushTimer;
复制代码
  1. #pragma mark - FlushTimer
  2. - (void)startFlushTimer {
  3.     if (self.flushTimer) {
  4.         return;
  5.     }
  6.     NSTimeInterval interval = self.flushInterval < 5 ? 5 : self.flushInterval;
  7.     self.flushTimer = [NSTimer timerWithTimeInterval:interval target:self selector:@selector(flush) userInfo:nil repeats:YES];
  8.     [NSRunLoop.currentRunLoop addTimer:self.flushTimer forMode:NSRunLoopCommonModes];
  9. }
  10. - (void)stopFlushTimer {
  11.     [self.flushTimer invalidate];
  12.     self.flushTimer = nil;
  13. }
复制代码
第三步:在 - initWithServerURL: 中调用开启定时器方法 - startFlushTimer
  1. - (instancetype)initWithServerURL:(NSString *)urlString {
  2.     self = [super init];
  3.     if (self) {
  4.         _automaticProperties = [self collectAutomaticProperties];
  5.         // 设置是否需是被动启动标记
  6.         _launchedPassively = UIApplication.sharedApplication.backgroundTimeRemaining != UIApplicationBackgroundFetchIntervalNever;
  7.         
  8.         _loginId = [[NSUserDefaults standardUserDefaults] objectForKey:SensorsAnalyticsLoginId];
  9.         
  10.         _trackTimer = [NSMutableDictionary dictionary];
  11.         
  12.         _enterBackgroundTrackTimerEvents = [NSMutableArray array];
  13.         
  14.         _fileStroe = [[SensorsAnalyticsFileStore alloc] init];
  15.         
  16.         _database = [[SensorsAnalyticsDatabase alloc] init];
  17.         
  18.         _network = [[SensorsAnalyticsNetwork alloc] initWithServerURL:[NSURL URLWithString:urlString]];
  19.         
  20.         NSString *queueLabel = [NSString stringWithFormat:@"cn.sensorsdata.%@.%p", self.class, self];
  21.         _serialQueue = dispatch_queue_create(queueLabel.UTF8String, DISPATCH_QUEUE_SERIAL);
  22.         
  23.         _flushBulkSize = 100;
  24.         
  25.         _flushInterval = 15;
  26.         
  27.         // 添加应用程序状态监听
  28.         [self setupListeners];
  29.         
  30.         [self startFlushTimer];
  31.     }
  32.     return self;
  33. }
复制代码
第四步:实现 - setFlushInterval: 方法
  1. - (void)setFlushInterval:(NSUInteger)flushInterval {
  2.     if (_flushInterval != flushInterval) {
  3.         _flushInterval = flushInterval;
  4.         // 上传本地缓存所有数据
  5.         [self flush];
  6.         // 先暂停定时器
  7.         [self stopFlushTimer];
  8.         // 重新开始定时器
  9.         [self startFlushTimer];
  10.     }
  11. }
复制代码
第五步:在 - applicationDidEnterBackground: 方法中停止定时器,在 - applicationDidBecomeActive: 中开启定时器
  1. - (void)applicationDidEnterBackground:(NSNotification *)notification {
  2.     NSLog(@"Application did enter background.");
  3.    
  4.     // 还原标记位
  5.     self.applicationWillResignActive = NO;
  6.    
  7.     // 触发 AppEnd 事件
  8.     // [self track:@"$AppEnd" properties:nil];
  9.     [self trackTimerEnd:@"$AppEnd" properties:nil];
  10.    
  11.     // 暂停所有事件时长统计
  12.     [self.trackTimer enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSDictionary * _Nonnull obj, BOOL * _Nonnull stop) {
  13.         if (![obj[@"is_pause"] boolValue]) {
  14.             [self.enterBackgroundTrackTimerEvents addObject:key];
  15.             [self trackTimerPause:key];
  16.         }
  17.     }];
  18.    
  19.     // 停止计时器
  20.     [self stopFlushTimer];
  21. }
  22. - (void)applicationDidBecomeActive:(NSNotification *)notification {
  23.     NSLog(@"Application did enter active.");
  24.    
  25.     // 还原标记位
  26.     if (self.applicationWillResignActive) {
  27.         self.applicationWillResignActive = NO;
  28.         return;
  29.     }
  30.    
  31.     // 将被动启动标记位设置为 NO,正常记录事件
  32.     self.launchedPassively = NO;
  33.    
  34.     // 触发 AppStart 事件
  35.     [self track:@"$AppStart" properties:nil];
  36.    
  37.     // 恢复所有的事件时长统计
  38.     for (NSString *event in self.enterBackgroundTrackTimerEvents) {
  39.         [self trackTimerStart:event];
  40.     }
  41.     [self.enterBackgroundTrackTimerEvents removeAllObjects];
  42.    
  43.     // 开始 $AppEnd 事件计时
  44.     [self trackTimerStart:@"$AppEnd"];
  45.    
  46.     // 开启定时器
  47.     [self startFlushTimer];
  48. }
复制代码
2.4 策略三

​        应用程序进入后天时尝试不同本地已缓存的所有数据
​        实现策略:通过 - beginBackgroundTaskWithExpirationHandler 方法,该方法可以让我们在应用程序进入后台最多有3分钟的时间来处理数据。
第一步:新增 - flushByEventCount: 方法 新增 background 参数,表示是否后台任务发起同步数据
  1. - (void)flushByEventCount:(NSUInteger) count background:(BOOL)background{
  2.     if (background) {
  3.         __block BOOL isContinue = YES;
  4.         dispatch_sync(dispatch_get_main_queue(), ^{
  5.             // 当运行时间大于请求超时时间时,为保证数据库删除时应用程序不被强杀,不在继续上传
  6.             isContinue = UIApplication.sharedApplication.backgroundTimeRemaining >= 30;
  7.         });
  8.         if (!isContinue) {
  9.             return;
  10.         }
  11.     }
  12.     // 获取本地数据
  13.     NSArray<NSString *> *events = [self.database selectEventsForCount:count];
  14.     // 当本地存储的数据为0或者上传 失败时,直接返回,退出递归调用
  15.     if (events.count == 0 || ![self.network flushEvents:events]) {
  16.         return;
  17.     }
  18.     // 当删除数据失败时,直接返回,退出递归调用,防止死循环
  19.     if ([self.database deleteEventsForCount:count]) {
  20.         return;
  21.     }
  22.     // 继续删除本地的其他数据
  23.     [self flushByEventCount:count background:background];
  24. }
复制代码
第二步:- flush 调用修改后的方法
  1. - (void)flush {
  2.     dispatch_async(self.serialQueue, ^{
  3.         // 默认向服务端一次发送 50 条数据
  4.         [self flushByEventCount:SensorsAnalyticsDefalutFlushEventCount background:NO];
  5.     });
  6. }
复制代码
第三步:- applicationDidEnterBackground: 添加后台同步数据的任务
  1. - (void)applicationDidEnterBackground:(NSNotification *)notification {
  2.     NSLog(@"Application did enter background.");
  3.         
  4.     // 还原标记位
  5.     self.applicationWillResignActive = NO;
  6.    
  7.     // 触发 AppEnd 事件
  8.     // [self track:@"$AppEnd" properties:nil];
  9.     [self trackTimerEnd:@"$AppEnd" properties:nil];
  10.    
  11.     UIApplication *application = UIApplication.sharedApplication;
  12.     // 初始化标记位
  13.     __block UIBackgroundTaskIdentifier backgroundTaskIdentifier = UIBackgroundTaskInvalid;
  14.     // 结束后台任务
  15.     void (^endBackgroundTast)(void) = ^() {
  16.         [application endBackgroundTask:backgroundTaskIdentifier];
  17.         backgroundTaskIdentifier = UIBackgroundTaskInvalid;
  18.     };
  19.    
  20.     // 标记长时间运行的后台任务
  21.     backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
  22.         endBackgroundTast();
  23.     }];
  24.    
  25.     dispatch_async(self.serialQueue, ^{
  26.         // 发送数据
  27.         [self flushByEventCount:SensorsAnalyticsDefalutFlushEventCount background:YES];
  28.         // 结束后台任务
  29.         endBackgroundTast();
  30.     });
  31.    
  32.     // 暂停所有事件时长统计
  33.     [self.trackTimer enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSDictionary * _Nonnull obj, BOOL * _Nonnull stop) {
  34.         if (![obj[@"is_pause"] boolValue]) {
  35.             [self.enterBackgroundTrackTimerEvents addObject:key];
  36.             [self trackTimerPause:key];
  37.         }
  38.     }];
  39.    
  40.     // 停止计时器
  41.     [self stopFlushTimer];
  42. }
复制代码
第四步:测试验证

来源:https://www.cnblogs.com/r360/p/16347174.html
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

笑看天下无敌手

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表