Да, каждый раз, когда вызывается метод setKeepAliveTimeout, я создаю новую конечную фоновую задачу (которая, кажется, запускается в течение 10 минут).

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

Я знаю, что этот подход немного отличается от того, который изображает 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)

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

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

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

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

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

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

 Jason Coco24 янв. 2011 г., 01:57
@valvoline: раз, когда это не работает, ваша задняя задача, очевидно, занимает слишком много времени. Документы говорят 30 секунд, но, возможно, это мягкое число, и ОС действительно дает вам 10 минут, я не уверен. Однако мы знаем, что в какой-то момент что-то блокируется, и ваша задача не может быть завершена, и, поскольку VIOP был разработан для быстрой проверки, вполне возможно, что обработка ошибок из ОС запуталась (например, не вызывая ваш блок отмены или что-то в этом роде) ,
 Aviad Ben Dov05 авг. 2011 г., 19:13
@jason: Есть ли у вас какие-либо подробности о том, что им требуется в этом окне VOIP? Это заявляет, что где-нибудь публично?
 Patrick Horn12 июн. 2012 г., 21:37
Для тех, кто интересуется тайм-аутами, RTFM здесь:developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/... В нем говорится, что между сообщениями активности должно быть не менее 600 секунд, и у вас есть только 10 секунд для выполнения необходимых действий, а не 30.
 valvoline24 янв. 2011 г., 01:50
большое спасибо за Ваш ответ. Прежде всего, отклонение не является проблемой, поскольку приложение является частным приложением, а не для AppStore. Что касается тайм-аута 30 секунд, я прочитал об этом, но я не могу понять, почему журнал сбоев сообщает мне о тайм-ауте 600,00 секунд, а не 30,00 секунд. Более того, иногда методы работают, а другие нет.
 Jason Coco13 июн. 2012 г., 02:28
@PatrickHorn Спасибо за добавление ссылки на документ. Когда этот ответ был написан, приложению было дано 30 секунд, чтобы выполнить необходимое обслуживание своего VoIP-сокета. Это было сокращено до 10 секунд с iOS 5. Приложениям, связанным с iOS 4 SDK, все еще дают 30 секунд, в то время как новым приложениям дают только 10.

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

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

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

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

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

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

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

 BadPirate25 сент. 2012 г., 19:31
Да, каждый раз, когда вызывается метод setKeepAliveTimeout, я создаю новую конечную фоновую задачу (которая, кажется, запускается в течение 10 минут).
 reekris24 сент. 2012 г., 12:14
Вы сбрасываете таймер выполнения вручную в обработчике «setKeepAliveTimeout»? Не могли бы вы предоставить пример кода для этого? Я в той же ситуации, использую VOIP (не собираюсь отправлять в AppStore). Мой таймер выполнения не сбрасывается автоматически, когда приложение разбудило setKeepAliveTimeout. Кажется, однако, что я могу сбросить его, создав новую фоновую задачу так же, как в моем «ApplicationDidEnterBackground».

Ваш ответ на вопрос