Я тоже видел это поведение.После долгих попыток я обнаружил две вещи, которые могли бы помочь.Но я все еще не уверен, как это может повлиять на процесс рецензирования.
Если вы используете одну из фоновых функций, приложение будет запущено iOS в фоновом режиме после его выхода (системой).Мы будем злоупотреблять этим позже.
В моем случае я использовал VoIP-фон, включенный в моем списке.Весь код здесь делается в вашем AppDelegate:
// if the iOS device allows background execution,
// this Handler will be called
- (void)backgroundHandler {
NSLog(@"### -->VOIP backgrounding callback");
// try to do sth. According to Apple we have ONLY 30 seconds to perform this Task!
// Else the Application will be terminated!
UIApplication* app = [UIApplication sharedApplication];
NSArray* oldNotifications = [app scheduledLocalNotifications];
// Clear out the old notification before scheduling a new one.
if ([oldNotifications count] > 0) [app cancelAllLocalNotifications];
// Create a new notification
UILocalNotification* alarm = [[[UILocalNotification alloc] init] autorelease];
if (alarm)
{
alarm.fireDate = [NSDate date];
alarm.timeZone = [NSTimeZone defaultTimeZone];
alarm.repeatInterval = 0;
alarm.soundName = @"alarmsound.caf";
alarm.alertBody = @"Don't Panic! This is just a Push-Notification Test.";
[app scheduleLocalNotification:alarm];
}
}
, а регистрация выполняется в
- (void)applicationDidEnterBackground:(UIApplication *)application {
// This is where you can do your X Minutes, if >= 10Minutes is okay.
BOOL backgroundAccepted = [[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{ [self backgroundHandler]; }];
if (backgroundAccepted)
{
NSLog(@"VOIP backgrounding accepted");
}
}
Теперь происходит волшебство: я даже не использую VoIP-сокеты.Но этот 10-минутный обратный вызов дает хороший побочный эффект: через 10 минут (иногда раньше) я обнаружил, что мои таймеры и предыдущие беговые дорожки выполняются в течение короткого времени.Вы можете увидеть это, если поместите в свой код NSLog (..).Это означает, что это короткое «пробуждение» некоторое время выполняет код.По словам Apple, у нас осталось 30 секунд выполнения.Я предполагаю, что фоновый код, такой как потоки, выполняется в течение почти 30 секунд.Это полезный код, если вам нужно «иногда» что-то проверять.
В документе сказано, что все фоновые задачи (VoIP, аудио, обновления местоположения) будут автоматически перезапущены в фоновом режиме, если приложение было прекращено.Приложения VoIP будут автоматически запускаться в фоновом режиме после загрузки!
Используя это поведение, вы можете заставить ваше приложение работать "навсегда".Зарегистрируйтесь для участия в одном фоновом процессе (например, VoIP).Это приведет к тому, что ваше приложение будет перезапущено после завершения.
Теперь напишите некоторый код «Задача должна быть выполнена».Согласно Apple, у вас есть некоторое время (5 секунд?), Чтобы закончить задание.Я обнаружил, что это должно быть процессорное время.Так что это означает: если вы ничего не делаете, ваше приложение все еще выполняется!Apple предложит вызвать обработчик истечения срока, если вы закончили свою работу.В коде ниже вы можете видеть, что у меня есть комментарий на expirationHandler.Это приведет к тому, что ваше приложение будет работать, пока система позволяет вашему приложению работать.Все таймеры и потоки будут работать до тех пор, пока iOS не закроет ваше приложение.
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// you can do sth. here, or simply do nothing!
// All your background treads and timers are still being executed
while (background)
[self doSomething];
// This is where you can do your "X minutes" in seconds (here 10)
sleep(10);
}
// And never call the expirationHandler, so your App runs
// until the system terminates our process
//[app endBackgroundTask:bgTask];
//bgTask = UIBackgroundTaskInvalid;
});
}
Будьте очень осторожны с CPU-Time здесь, и ваше приложение будет работать дольше!Но одно можно сказать наверняка: ваше приложение будет закрыто через некоторое время.Но поскольку вы зарегистрировали свое приложение как VoIP или одно из других, система перезапускает приложение в фоновом режиме, что перезапустит ваш фоновый процесс ;-) С помощью этого PingPong я могу выполнять много фоновых операций.но помните, что нужно уделять больше времени процессору.И сохраните все данные, чтобы восстановить ваши представления - ваше приложение будет закрыто через некоторое время.Чтобы он все еще работал, вы должны вернуться в свое последнее «состояние» после пробуждения.
Я не знаю, является ли это подходом приложений, о которых вы упоминали ранее, но это работает для меня.
Надеюсь, что смогу помочь
Обновление:
После измерения времени задания BG произошел сюрприз.Задание BG ограничено 600 секундами.Это точное минимальное время VoIP минимального времени (setKeepAliveTimeout: 600).
Таким образом, ЭТОТ код приводит к «бесконечному» выполнению в фоновом режиме:
Заголовок:
UIBackgroundTaskIdentifier bgTask;
Код:
// if the iOS device allows background execution,
// this Handler will be called
- (void)backgroundHandler {
NSLog(@"### -->VOIP backgrounding callback");
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(@"BGTime left: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self doSomething];
sleep(1);
}
});
- (void)applicationDidEnterBackground:(UIApplication *)application {
BOOL backgroundAccepted = [[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{ [self backgroundHandler]; }];
if (backgroundAccepted)
{
NSLog(@"VOIP backgrounding accepted");
}
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(@"BGTime left: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self doSomething];
sleep(1);
}
});
}
После истечения времени ожидания вашего приложения будет вызван expirationHandler VoIP, где вы просто перезапустите долгосрочную задачу.Эта задача будет завершена через 600 секунд.Но снова будет вызов обработчика срока действия, который запускает еще одну долгосрочную задачу и т. Д. Теперь вам нужно только проверить, возвращается ли приложение на передний план.Затем закройте bgTask, и все готово.Может быть, можно сделать что-нибудькак это внутри expirationHandler из долгосрочной задачи.Просто попробуйте.Используйте консоль, чтобы увидеть, что происходит ... Развлекайтесь!
Обновление 2:
Иногда упрощение помогает.Мой новый подход такой:
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
// it's better to move "dispatch_block_t expirationHandler"
// into your headerfile and initialize the code somewhere else
// i.e.
// - (void)applicationDidFinishLaunching:(UIApplication *)application {
//
// expirationHandler = ^{ ... } }
// because your app may crash if you initialize expirationHandler twice.
dispatch_block_t expirationHandler;
expirationHandler = ^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
};
bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// inform others to stop tasks, if you like
[[NSNotificationCenter defaultCenter] postNotificationName:@"MyApplicationEntersBackground" object:self];
// do your background work here
});
}
Это работает без взлома VoIP. Согласно документации, обработчик истечения (в данном случае мой блок 'expirationHandler') будет выполнен, если время выполнения истекло. Определив блок в переменной блока, можно рекурсивно запустить долгосрочную задачу снова в обработчике срока действия. Это также приводит к бесконечному исполнению.
Будьте внимательны, чтобы завершить задачу, если ваше приложение снова выходит на передний план. И прекратите задачу, если она вам больше не нужна.
Для собственного опыта я что-то измерил.
Использование обратных вызовов с включенным GPS-приемником очень быстро разряжает мою батарею. Использование подхода, который я выложил в Обновление 2 , почти не требует энергии. Согласно «опыту использования», это лучший подход. Может быть, другие приложения работают так, скрывая свое поведение за функциями GPS ...