From 3b7fcbc1b5a28d7cccb5a5320f14b874ba5d6fd6 Mon Sep 17 00:00:00 2001 From: Yuhan ZOU Date: Thu, 31 Mar 2016 11:42:47 +0800 Subject: [PATCH] Release 1.3.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复 Flush 时线程冲突的问题 --- .../SensorsAnalyticsSDK.podspec | 2 +- .../SensorsAnalyticsSDK/SensorsAnalyticsSDK.m | 316 +++++++++--------- 2 files changed, 166 insertions(+), 152 deletions(-) diff --git a/SensorsAnalyticsSDK/SensorsAnalyticsSDK.podspec b/SensorsAnalyticsSDK/SensorsAnalyticsSDK.podspec index b911d74a..69bc33e1 100644 --- a/SensorsAnalyticsSDK/SensorsAnalyticsSDK.podspec +++ b/SensorsAnalyticsSDK/SensorsAnalyticsSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SensorsAnalyticsSDK" - s.version = "1.3.7" + s.version = "1.3.8" s.summary = "The offical iOS SDK of Sensors Analytics." s.homepage = "http://www.sensorsdata.cn" s.source = { :git => 'https://github.com/sensorsdata/sa-sdk-ios.git', :tag => "v#{s.version}" } diff --git a/SensorsAnalyticsSDK/SensorsAnalyticsSDK/SensorsAnalyticsSDK.m b/SensorsAnalyticsSDK/SensorsAnalyticsSDK/SensorsAnalyticsSDK.m index b59000d5..74a7292c 100755 --- a/SensorsAnalyticsSDK/SensorsAnalyticsSDK/SensorsAnalyticsSDK.m +++ b/SensorsAnalyticsSDK/SensorsAnalyticsSDK/SensorsAnalyticsSDK.m @@ -26,7 +26,7 @@ #import "SASwizzler.h" #import "SensorsAnalyticsSDK.h" -#define VERSION @"1.3.7" +#define VERSION @"1.3.8" @implementation SensorsAnalyticsDebugException @@ -221,120 +221,129 @@ - (void)flush { flushSize = 1; } - NSArray *recordArray = [self.messageQueue getFirstRecords:flushSize]; - if (recordArray == nil) { - @throw [NSException exceptionWithName:@"SqliteException" - reason:@"getFirstRecords from Message Queue in Sqlite fail" - userInfo:nil]; - } - - while ([recordArray count] > 0) { - // 1. 先完成这一系列Json字符串的拼接 - NSString *jsonString = [NSString stringWithFormat:@"[%@]",[recordArray componentsJoinedByString:@","]]; - // 2. 使用gzip进行压缩 - NSData *zippedData = [LFCGzipUtility gzipData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]]; - // 3. base64 - NSString *b64String = [zippedData sa_base64EncodedString]; - b64String = (id)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, - (CFStringRef)b64String, - NULL, - CFSTR("!*'();:@&=+$,/?%#[]"), - kCFStringEncodingUTF8)); - - NSString *postBody = [NSString stringWithFormat:@"gzip=1&data_list=%@", b64String]; - - NSURL *URL = [NSURL URLWithString:self.serverURL]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]]; - [request setValue:@"SensorsAnalytics iOS SDK" forHTTPHeaderField:@"User-Agent"]; - if (_debugMode == SensorsAnalyticsDebugOnly) { - [request setValue:@"true" forHTTPHeaderField:@"Dry-Run"]; + dispatch_async(self.serialQueue, ^{ + NSArray *recordArray = [self.messageQueue getFirstRecords:flushSize]; + if (recordArray == nil) { + @throw [NSException exceptionWithName:@"SqliteException" + reason:@"getFirstRecords from Message Queue in Sqlite fail" + userInfo:nil]; } - dispatch_semaphore_t flushSem = dispatch_semaphore_create(0); - __block BOOL flushError = NO; - - void (^block)(NSData*, NSURLResponse*, NSError*) = ^(NSData *data, NSURLResponse *response, NSError *error) { - if (error) { - NSString *errMsg = [NSString stringWithFormat:@"%@ network failure: %@", self, error]; - if (_debugMode != SensorsAnalyticsDebugOff) { - @throw [SensorsAnalyticsDebugException exceptionWithName:@"NetworkException" - reason:errMsg - userInfo:nil]; - } else { - SAError(@"%@", errMsg); - } - flushError = YES; - return; - } + while ([recordArray count] > 0) { + // 1. 先完成这一系列Json字符串的拼接 + NSString *jsonString = [NSString stringWithFormat:@"[%@]",[recordArray componentsJoinedByString:@","]]; + // 2. 使用gzip进行压缩 + NSData *zippedData = [LFCGzipUtility gzipData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]]; + // 3. base64 + NSString *b64String = [zippedData sa_base64EncodedString]; + b64String = (id)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + (CFStringRef)b64String, + NULL, + CFSTR("!*'();:@&=+$,/?%#[]"), + kCFStringEncodingUTF8)); - if (![response isKindOfClass:[NSHTTPURLResponse class]]) { - flushError = YES; - return; + NSString *postBody = [NSString stringWithFormat:@"gzip=1&data_list=%@", b64String]; + + NSURL *URL = [NSURL URLWithString:self.serverURL]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]]; + [request setValue:@"SensorsAnalytics iOS SDK" forHTTPHeaderField:@"User-Agent"]; + if (_debugMode == SensorsAnalyticsDebugOnly) { + [request setValue:@"true" forHTTPHeaderField:@"Dry-Run"]; } - NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse*)response; - if([urlResponse statusCode] != 200) { - NSString *urlResponseContent = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSString *errMsg = [NSString stringWithFormat:@"%@ flush failure with response '%@'.", self, urlResponseContent]; - if (_debugMode != SensorsAnalyticsDebugOff) { - SALog(@"=========================================================================="); - SALog(@"%@ invalid message: %@", self, jsonString); - SALog(@"%@ ret_code: %ld", self, [urlResponse statusCode]); - SALog(@"%@ ret_content: %@", self, urlResponseContent); - - @throw [SensorsAnalyticsDebugException exceptionWithName:@"IllegalDataException" - reason:errMsg - userInfo:nil]; - } else { - SAError(@"%@", errMsg); + dispatch_semaphore_t flushSem = dispatch_semaphore_create(0); + __block BOOL flushError = NO; + + void (^block)(NSData*, NSURLResponse*, NSError*) = ^(NSData *data, NSURLResponse *response, NSError *error) { + if (error) { + NSString *errMsg = [NSString stringWithFormat:@"%@ network failure: %@", self, error]; + if (_debugMode != SensorsAnalyticsDebugOff) { + @throw [SensorsAnalyticsDebugException exceptionWithName:@"NetworkException" + reason:errMsg + userInfo:nil]; + } else { + SAError(@"%@", errMsg); + } flushError = YES; return; } - } else { - if (_debugMode != SensorsAnalyticsDebugOff) { - SALog(@"=========================================================================="); - SALog(@"%@ valid message: %@", self, jsonString); + + if (![response isKindOfClass:[NSHTTPURLResponse class]]) { + flushError = YES; + return; + } + + NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse*)response; + if([urlResponse statusCode] != 200) { + NSString *urlResponseContent = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSString *errMsg = [NSString stringWithFormat:@"%@ flush failure with response '%@'.", self, urlResponseContent]; + if (_debugMode != SensorsAnalyticsDebugOff) { + SALog(@"=========================================================================="); + SALog(@"%@ invalid message: %@", self, jsonString); + SALog(@"%@ ret_code: %ld", self, [urlResponse statusCode]); + SALog(@"%@ ret_content: %@", self, urlResponseContent); + + @throw [SensorsAnalyticsDebugException exceptionWithName:@"IllegalDataException" + reason:errMsg + userInfo:nil]; + } else { + SAError(@"%@", errMsg); + flushError = YES; + return; + } + } else { + if (_debugMode != SensorsAnalyticsDebugOff) { + SALog(@"=========================================================================="); + SALog(@"%@ valid message: %@", self, jsonString); + } } + + dispatch_semaphore_signal(flushSem); + }; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:block]; + + [task resume]; +#else + [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler: + ^(NSURLResponse *response, NSData* data, NSError *error) { + return block(data, response, error); + }]; +#endif + + dispatch_semaphore_wait(flushSem, DISPATCH_TIME_FOREVER); + + if (flushError) { + break; } - dispatch_semaphore_signal(flushSem); - }; - - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:block]; - - [task resume]; - - dispatch_semaphore_wait(flushSem, DISPATCH_TIME_FOREVER); - - if (flushError) { - break; + if (![self.messageQueue removeFirstRecords:flushSize]) { + @throw [NSException exceptionWithName:@"SqliteException" + reason:@"removeFirstRecords from Message Queue in Sqlite fail" + userInfo:nil]; + } + recordArray = [self.messageQueue getFirstRecords:flushSize]; + if (recordArray == nil) { + @throw [NSException exceptionWithName:@"SqliteException" + reason:@"getFirstRecords from Message Queue in Sqlite fail" + userInfo:nil]; + } + + SADebug(@"flush one batch success, currentCount is %lu", [self.messageQueue count]); } - if (![self.messageQueue removeFirstRecords:flushSize]) { - @throw [NSException exceptionWithName:@"SqliteException" - reason:@"removeFirstRecords from Message Queue in Sqlite fail" - userInfo:nil]; - } - recordArray = [self.messageQueue getFirstRecords:flushSize]; - if (recordArray == nil) { + if (![self.messageQueue vacuum]) { @throw [NSException exceptionWithName:@"SqliteException" - reason:@"getFirstRecords from Message Queue in Sqlite fail" + reason:@"vacuum in Message Queue in Sqlite fail" userInfo:nil]; } - SADebug(@"flush one batch success, currentCount is %lu", [self.messageQueue count]); - } - - if (![self.messageQueue vacuum]) { - @throw [NSException exceptionWithName:@"SqliteException" - reason:@"vacuum in Message Queue in Sqlite fail" - userInfo:nil]; - } - - _lastFlushTime = [[self class] getCurrentTime]; + _lastFlushTime = [[self class] getCurrentTime]; + }); } - (BOOL) isValidName : (NSString *) name { @@ -949,63 +958,68 @@ - (void)applicationDidEnterBackground:(NSNotification *)notification { #pragma mark - SensorsData VTrack Analytics - (void)checkForConfigure { - dispatch_async(self.serialQueue, ^{ - SADebug(@"%@ starting configure check", self); - - if (self.configureURL == nil || self.configureURL.length < 1) { + SADebug(@"%@ starting configure check", self); + + if (self.configureURL == nil || self.configureURL.length < 1) { + return; + } + + void (^block)(NSData*, NSURLResponse*, NSError*) = ^(NSData *data, NSURLResponse *response, NSError *error) { + if (error) { + SAError(@"%@ decide check http error: %@", self, error); return; } - NSURL *URL = [NSURL URLWithString:self.configureURL]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; - [request setHTTPMethod:@"GET"]; - - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *task = [session dataTaskWithRequest:request - completionHandler: - ^(NSData *data, NSURLResponse *response, NSError *error) { - if (error) { - SAError(@"%@ decide check http error: %@", self, error); - return; - } - - NSError *parseError; - NSDictionary *object = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError]; - if (parseError) { - SAError(@"%@ decide check json error: %@, data: %@", self, error, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); - return; - } - - NSDictionary *rawEventBindings = object[@"event_bindings"]; - if (rawEventBindings && [rawEventBindings isKindOfClass:[NSDictionary class]]) { - NSArray *eventBindings = rawEventBindings[@"events"]; - if (eventBindings && [eventBindings isKindOfClass:[NSArray class]]) { - // Finished bindings are those which should no longer be run. - [self.eventBindings makeObjectsPerformSelector:NSSelectorFromString(@"stop")]; - - NSMutableSet *parsedEventBindings = [NSMutableSet set]; - for (id obj in eventBindings) { - SAEventBinding *binding = [SAEventBinding bindingWithJSONObject:obj]; - if (binding) { - [binding execute]; - [parsedEventBindings addObject:binding]; - } - } - - SALog(@"%@ found %lu tracking events: %@", self, (unsigned long)[parsedEventBindings count], parsedEventBindings); - - self.eventBindings = parsedEventBindings; - [self archiveEventBindings]; - } else { - SALog(@"%@ Tracking events check response format error: %@", self, object); - } - } else { - SALog(@"%@ Tracking events check response format error: %@", self, object); - } - }]; + NSError *parseError; + NSDictionary *object = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError]; + if (parseError) { + SAError(@"%@ decide check json error: %@, data: %@", self, error, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); + return; + } - [task resume]; - }); + NSDictionary *rawEventBindings = object[@"event_bindings"]; + if (rawEventBindings && [rawEventBindings isKindOfClass:[NSDictionary class]]) { + NSArray *eventBindings = rawEventBindings[@"events"]; + if (eventBindings && [eventBindings isKindOfClass:[NSArray class]]) { + // Finished bindings are those which should no longer be run. + [self.eventBindings makeObjectsPerformSelector:NSSelectorFromString(@"stop")]; + + NSMutableSet *parsedEventBindings = [NSMutableSet set]; + for (id obj in eventBindings) { + SAEventBinding *binding = [SAEventBinding bindingWithJSONObject:obj]; + if (binding) { + [binding execute]; + [parsedEventBindings addObject:binding]; + } + } + + SALog(@"%@ found %lu tracking events: %@", self, (unsigned long)[parsedEventBindings count], parsedEventBindings); + + self.eventBindings = parsedEventBindings; + [self archiveEventBindings]; + } else { + SALog(@"%@ Tracking events check response format error: %@", self, object); + } + } else { + SALog(@"%@ Tracking events check response format error: %@", self, object); + } + }; + + NSURL *URL = [NSURL URLWithString:self.configureURL]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; + [request setHTTPMethod:@"GET"]; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:block]; + + [task resume]; +#else + [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler: + ^(NSURLResponse *response, NSData* data, NSError *error) { + return block(data, response, error); + }]; +#endif } - (void)connectToVTrackDesigner {