2014-10-01 3 views
1

서비스 연결을 유지하고 이벤트 (호출)를 받기 위해 긴 폴링 메커니즘을 사용하는 VoIP iOS 앱이 있습니다. 즉, NSURLConnection은 몇 분 동안 보류 중이지만 이벤트가 발생한 직후에 반환됩니다. VoIP 플래그로 인해, 앱이 백그라운드 모드 인 동안 Keep Alive 핸들러를 설정하고 업데이트를받을 수 있습니다.iOS : NSURLConnection 콜백이 많이 지연되거나 실행되지 않습니다.

그러나 대부분의 경우이 작업은 이지만 신뢰할 수는 없습니다.. 경우에 따라 NSURLConnection 콜백이 너무 지연되거나 요청이 시간 초과 된 후에도 전혀 실행되지 않을 수 있습니다 (timeoutIntervalNSURLRequest에 도달).

로그의 예는 명확히 :

    이 앱은 백그라운드 모드
  • NSURLConnection # 1 (긴 설문 조사) (부팅시 시스템에 의해 시작됩니다)에서 실행
  • 이 시작 반환 후 1 새로운 데이터
  • NSURLConnection # 2 (긴 설문 조사)와 분 시작되고 15 일 이후 수익률은 새로운 데이터없이 (서버 측 최대) 의사록
  • (...)
  • 01,230,834,878,966,146,532 10 # 99 (long poll)이 시작되지만 복귀하지 않습니다. timeoutInterval이 만료 된 후에도 (16 분)
  • 때때로 keep alive 핸들러가 호출되며 아무 것도 발생하지 않습니다. backgroundTimeRemaining 속성의 값이 비현실적입니다 (180.0 대신 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0).
  • 1 시간 후 사용자가 앱을 엽니 다. 이 앱은 NSURLRequest들과 receivce 응답 일부 secounds 후
  • 다양한 실행할 수, 사용자가 10 분 이상 후 응용 프로그램
  • 을 닫고는 NSURLConnection # 99 콜백 didFailWithError는 시간 초과 오류 (-1001)로 해고. 이 요청의 실행 시간은 심지어 timeoutInterval의 경우 16 분으로 제한되어 있었지만 이후에 초기화되었지만 이전에 완료된 다른 여러 요청에 한 시간 이상 걸렸습니다.

내 관점에서 볼 때, 이것은 iOS의 매우 이상한 행동처럼 보입니다. 왜 iOS가 백그라운드 실행 시간을 앱에 제공하고 Keep Alive 핸들러를 호출해야하지만 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의 응답 시간은 중요합니다.