2014-10-01 62 views
1

我得到一個VoIP iOS應用程序使用長輪詢機制來維護其服務連接並接收事件(呼叫)。這意味着,NSURLConnection正在等待幾分鐘,但會在事件發生後立即返回。由於VoIP標誌,即使應用程序處於後臺模式,也可以設置保持活動處理程序並接收更新。iOS:NSURLConnection回調嚴重延遲或根本沒有被解僱

但是,這大多數時間工作但不可靠。有時,即使在請求超時(到達NSURLRequesttimeoutInterval)之後,NSURLConnection回調也會嚴重延遲或根本沒有被解僱。

從日誌中的例子來闡明:

  • 該應用程序在後臺模式下
  • NSURLConnection#1(長輪詢)(由系統在引導時啓動)運行被啓動,並且返回後1一些新的數據
  • NSURLConnection#2(長輪詢)分鐘開始和15後的回報議事錄(服務器端最大),沒有任何新的數據
  • (...)
  • NSURLConnection#99(長輪詢)已啓動,但不會返回 - 即使在timeoutInterval已過期(16分鐘)之後也沒有。
  • 有時候,保持活動處理程序被調用,bot沒有任何反應。該backgroundTimeRemaining財產得到了不切實際的高價值(179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0而不是最大180.0)。
  • 1小時後,用戶打開應用程序。該應用程序能夠執行各種NSURLRequest S和receivce反應
  • 一些secounds後,用戶關閉應用程序
  • 10多分鐘後,NSURLConnection#99回調didFailWithError得到了與超時錯誤(-1001)解僱。這個請求的執行時間超過了一個小時,甚至很難將timeoutInterval限制在16分鐘以內,還有幾個其他的請求在稍後啓動,但是之前完成。

從我的角度來看,這似乎是iOS的一個非常奇怪的行爲。爲什麼iOS應用程序需要後臺執行時間並調用保持活動處理程序,但是沒有及時正確地觸發NSURLConnection回調?

保持活動的處理程序:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 

    [[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{ 

     NSLog(@"########### Started Keep-Alive Handler ###########"); 

     [self startBackgroundHandler:YES timeout:30]; 

     NSLog(@"########### Completed Keep-Alive Handler ###########"); 

    }]; 

    [self startBackgroundHandler:NO timeout:60]; 

} 

-(void)startBackgroundHandler:(BOOL)force timeout:(int)timeout { 
    UIApplicationState currentAppState = [[UIApplication sharedApplication] applicationState]; 
    BOOL appIsBackground = currentAppState == UIApplicationStateBackground; 
    if(appIsBackground || force) { 

     int localThreadId = ++_currentBackgroundThreadId; 

     __block UIBackgroundTaskIdentifier bgTask; 
     bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 
      // Clean up any unfinished task business by marking where you 
      // stopped or ending the task outright. 
      NSLog(@"Cleaning up [Background Thread %d] ...", localThreadId); 
      [[UIApplication sharedApplication] endBackgroundTask:bgTask]; 
      bgTask = UIBackgroundTaskInvalid; 
     }]; 

     NSLog(@"startBackgroundHandler with [Background Thread %d] appIsBackground=%d force=%d", localThreadId, appIsBackground, force); 

     // Start the long-running task and return immediately. 
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

      if(_currentBackgroundThreadId == localThreadId) { 
       NSLog(@"[Background Thread %d] Background time left: %0.1f", localThreadId, [UIApplication 
                sharedApplication].backgroundTimeRemaining); 
       sleep(timeout); 
      } 

      NSLog(@"[Background Thread %d] Will exit...", localThreadId); 
      [[UIApplication sharedApplication] endBackgroundTask:bgTask]; 
      bgTask = UIBackgroundTaskInvalid; 
     }); 
    } else { 
     NSLog(@"Ignored startBackgroundHandler - appIsBackground=%d force=%d", appIsBackground, force); 
    } 
} 

所有NSURLConnections有runloop - 它們如下啓動:

NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:mrequest delegate:self startImmediately:NO]; 
if(connection) { 
    [connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 
    [connection start]; 

} else { 
    // error handling... 
} 

PS: 在應用程序的早期版本,則使用data fetch背景模式而不是voip,並且從未遇到過這種問題。

回答

1

似乎像iOS一樣不願意在主線程中爲您提供CPU時間,而您在後臺 - 即使您獲得了VoIP標誌。因此,您應該在一個單獨的線程中安排您的請求,並使用CFRunLoopRun()使其在後臺運行。此外,您必須使用runloop上的runMode:beforeDate:來觸發執行適當的模式。

但是真正的問題是,如果請求太頻繁地超時,iOS會停止給CPU時間 - 你真的需要接收任何東西以獲得CPU時間。因此,讓WebServer及時回覆非常重要。