Ответ 1
Я тоже видел это поведение. Пробовав много, я обнаружил две вещи, которые могли бы помочь. Но я все еще не уверен, как это может повлиять на процесс рассмотрения.
Если вы используете одну из функций фонового рисунка, приложение будет запускаться iOS в фоновом режиме снова после его выхода (системой). Это мы будем оскорблять позже.
В моем случае я использовал фоновое соединение VoIP в моем plist. Весь код здесь выполняется в 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 я могу многое сделать. но помните, что очень экономьте время процессора. И сохраните все данные, чтобы восстановить свои представления - ваше приложение будет прекращено через некоторое время. Чтобы он все еще работал, вы должны вернуться в свое последнее состояние после пробуждения.
Я не знаю, является ли это подход приложений, о которых вы говорили ранее, но он работает для меня.
Надеюсь, что смогу помочь
Update:
После измерения времени задания 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);
}
});
}
После истечения времени ожидания вашего приложения вызывается VoIP expirationHandler, где вы просто перезапускаете многолетнюю задачу. Эта задача будет прекращена через 600 секунд. Но снова будет вызов обработчику истечения срока действия, который запустит еще одну длинную задачу и т.д. Теперь вам нужно только проверить погоду, когда приложение возвращается на передний план. Затем закройте bgTask, и все готово. Может быть, можно это сделать. как это происходит в expirationHandler из долговременной задачи. Просто попробуйте. Используйте консоль, чтобы узнать, что произойдет... Удовольствие!
Обновление 2:
Иногда упрощается то, что помогает. Мой новый подход заключается в следующем:
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
// it 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, почти не имеет энергии. Согласно "userexperience", это лучший подход. Возможно, другие приложения работают так, скрывая свое поведение за функциональностью GPS...