setKeepAliveTimeout и BackgroundTasks - PullRequest
       22

setKeepAliveTimeout и BackgroundTasks

8 голосов
/ 24 января 2011

У меня большая головная боль с темой.Я работаю над приложением, которое должно регулярно опрашивать веб-сервер, чтобы проверять наличие новых данных.Основываясь на возвращенной информации, я хочу отправить локальное уведомление пользователю.

Я знаю, что этот подход немного отличается от того, который изображен Apple, в котором удаленный сервер выполняет работу, выдвигая удаленныйуведомление, основанное на APNS.Однако есть много причин, по которым я не могу принять этот подход во внимание.Одним из всех является механизм аутентификации пользователя.Удаленный сервер по соображениям безопасности не может учитывать учетные данные пользователя.Все, что я могу сделать, - это перенести ядро ​​входа и загрузки на клиент (iPhone).

Я заметил, что Apple предоставляет возможность приложениям активизироваться и держать открытым соединение Socket (т.е.Приложение VoIP).

Итак, я начал расследование таким образом.Добавил необходимую информацию в plist, я могу "разбудить" мое приложение, используя что-то вроде этого в моем appDelegate:

[[UIApplication sharedApplication] setKeepAliveTimeout:1200 handler:^{ 
    NSLog(@"startingKeepAliveTimeout");
    [self contentViewLog:@"startingKeepAliveTimeout"];
    MyPushOperation *op = [[MyPushOperation alloc] initWithNotificationFlag:0 andDataSource:nil];
    [queue addOperation:op];
    [op release];
}];

NSOperation, а затем запускает фоновую задачу, используя следующий код блока:

#pragma mark SyncRequests
-(void) main {
    NSLog(@"startSyncRequest");
    [self contentViewLog:@"startSyncRequest"];
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ 
        NSLog(@"exipiration handler triggered");
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
        [self cancel];
    }];


        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSMutableURLRequest *anURLRequest;
            NSURLResponse *outResponse;
            NSError *exitError;
            NSString *username;
            NSString *password;

            NSLog(@"FirstLogin");
            anURLRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:webserverLogin, username, password]]];
            [anURLRequest setHTTPMethod:@"GET"];
            [anURLRequest setTimeoutInterval:120.00];
            [anURLRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];

            exitError = nil;
            NSData *tmpData = [NSURLConnection sendSynchronousRequest:anURLRequest returningResponse:&outResponse error:&exitError];
            [anURLRequest setTimeoutInterval:120.00];
            if(exitError != nil) { //somethings goes wrong
                NSLog(@"somethings goes wrong");
                [app endBackgroundTask:bgTask];
                bgTask = UIBackgroundTaskInvalid;
                [self cancel];
                return;
            }

            //do some stuff with NSData and prompt the user with a UILocalNotification

            NSLog(@"AlltasksCompleted");
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
            [self cancel];
        });
    }
}

Вышеприведенный код, кажется, работает (иногда), но во многих других случаях это приводит к сбою моего приложения со следующей информацией журнала:

Exception Type:  00000020
Exception Codes: 0x8badf00d
Highlighted Thread:  3

Application Specific Information:
DemoBackApp[5977] has active assertions beyond permitted time: 
{(
    <SBProcessAssertion: 0xa9da0b0> identifier: UIKitBackgroundCompletionTask process: DemoBackApp[5977] permittedBackgroundDuration: 600.000000 reason: finishTask owner pid:5977 preventSuspend  preventIdleSleep 
)}

Elapsed total CPU time (seconds): 0.010 (user 0.010, system 0.000), 100% CPU 
Elapsed application CPU time (seconds): 0.000, 0% CPU

Для тех, кто спрашивает, да.Я тоже попробовал подход Async NSURLConnection.Независимо от того.Сбой тот же, даже если я использую асинхронный подход с обработчиком тайм-аута и didFinishLoading: WithError.

Я застрял.Любые намеки высоко ценятся.

Ответы [ 4 ]

9 голосов
/ 18 апреля 2013

Это старая ветка, но может потребовать обновления.

Начиная с iOS 6, это поведение, которое я наблюдаю с фоновыми методами таймера VoIP, как обсуждалось в этой теме:

  • Фоновый режим VoIP по-прежнему строго запрещен для приложений AppStore через процесс обзора приложений
  • Минимальное время KeepAlive составляет 600 секунд; все, что меньше этого, вызовет сбой установки обработчика (и в NSLog будет отправлено предупреждение)
  • Установка времени keepAlive, значительно превышающего 600 секунд, обычно приводит к тому, что обработчик запускается с частотой каждый интервал time / 2 . Бонусный факт: это согласуется с запросом SIP REGISTER, в котором рекомендуемый интервал перерегистрации составляет .5 * время перерегистрации.
  • Когда вызывается ваш обработчик keepAlive, я заметил следующее:
    • Вы получаете 10 секунд времени выполнения "переднего плана", в течение которого оставшееся время фона бесконечно (как возвращено backgroundTimeRemaining )
    • Если вы запустили beginBackgroundTask из обработчика keepAlive, я заметил, что вы получаете 60 секунд времени выполнения фона (как возвращено backgroundTimeRemaining ). Это отличается от 600 секунд , которые вы получаете, когда пользователь переходит из вашего приложения в активное состояние на задний план. Я не нашел способа продлить это время (без использования других хитростей, подобных расположению и др.)

Надеюсь, это поможет!

7 голосов
/ 24 января 2011

Когда вы звоните -setKeepAliveTimeout:handler:, вы получаете максимум 30 секунд, чтобы завершить все и приостановить.Вам не предоставляется тот же фоновый льготный период , который вы получили бы при первом переходе приложения в фоновый режим.Это предназначено для завершения длительных задач, выключения и т. Д.

С обратным вызовом VOIP вы просто должны отправить любой пакет ping, который вам нужно отправить в службу, чтобы поддерживать живое сетевое соединение ине истекло время ожиданияЧерез 30 секунд, независимо от запуска новых фоновых задач, если ваше приложение все еще выполняется, вы будете прерваны.

Кроме того, важно отметить, что если вы на самом деле не являетесь приложением VOIP или если вы сделаете что-либо в течение окна обратного вызова VOIP, которое не связано с сохранением открытых сетевых подключений, ваше приложение будет отклонено из магазина приложений.Когда вы устанавливаете любой из флагов активности (VOIP, фоновая музыка, навигация), они довольно тщательно проверяют его, чтобы убедиться, что он только делает то, что он помечен, чтобы делать в фоновом режиме.Выполнение любого вида HTTP-запроса GET и ожидание возврата большого объема данных почти гарантированно отклонят ваше приложение.

РЕДАКТИРОВАТЬ: Как отметил Патрик в комментариях, текущее количество времени блокавремя выполнения было сокращено с 30 секунд до 10 секунд в iOS 5. Это хорошая идея, чтобы следить за этим временем всякий раз, когда вы повторно связываете свое приложение для новой версии SDK, всегда по крайней мере быстро проверяйте документы весли они были обновлены (с выходом iOS 6, это число может быть снова изменено).

6 голосов
/ 30 сентября 2013

Просто чтобы обновить это поведение для iOS7.

  • Когда ваше приложение впервые переходит в фоновый режим, вы получаете 180 секунд времени на выполнение задания, как сообщает backgroundTimeRemaining.Однако он перестает отвечать за 5 секунд до того, как истечет это время, как сообщается backgroundTimeRemaining.
  • Когда запускается задача keepAlive, то backgroundTimeRemaining составляет 10 секунд переднего плана, а затем 60 секунд таймера фона, как сообщается backgroundTimeRemaining.Он также перестает отвечать на запросы за 5 секунд до истечения этого времени, как сообщает backgroundTimeRemaining.

Так что на iOS7 вы можете получать 65 секунд времени обработки каждые 10 минут.

3 голосов
/ 07 сентября 2012

Кажется, что вы можете комбинировать обработчик таймаута keep alive, поддерживая запрос на конечное выполнение фоновой задачи при вызове.Это даст вам полные 10 минут каждый раз, когда вызывается обработчик поддержки VOIP (т. Е. Обычно 10-30 секунд).

Все та же проблема, что и выше - в том, что вам нужен флаг VOIP в вашем спискеотправить, и Apple вряд ли примет ваше приложение, если у вас есть этот флаг и вы на самом деле не являетесь приложением VOIP, но для внутреннего распространения (корпоративного или иного) это решение должно хорошо работать, чтобы дать вам фоновое время.

В моих тестах 10-минутный таймер конечного выполнения сбрасывается каждый раз, когда вызывается обработчик VOIP (вне зависимости от того, перенес ли пользователь приложение с тех пор), и это должно означать, что вы можете опрашивать ваш сервер бесконечнов то время как в фоновом режиме каждые 600 секунд (10 минут) и процесс опроса может занять до 10 минут перед сном (что означает почти постоянную работу в фоновом режиме, если это то, что вам нужно).

Опять же, не совсемвариант для App Store, если вы не можете убедитьм ты VOIP.

...