Как отменить рабочий поток в applicationDidEnterBackground на iOS - PullRequest
2 голосов
/ 18 марта 2011

Я тщательно искал подходящее решение, но оно не сработало: Приложение для iPhone обновляет данные через запросы NSURL по требованию пользователя. Размер каждого загружаемого файла составляет всего 1,5 КБ, но одно обновление может содержать 400 таких файлов. Поэтому я делаю загрузку в отдельном потоке, который можно отменить, и во время обновления есть UIAlertView с индикацией процесса и кнопкой отмены. Это может продолжаться 1 ... 3 минуты, поэтому оно может превысить время ожидания, пока устройство остается в живых или произойдет что-то другое, и мое приложение перейдет в фоновый режим.

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

diagView*   viewDiagCtrl = startDiag.diagViewControl;

if (viewDiagCtrl != nil && viewDiagCtrl.actionThread != nil)
{
    // UIBackgroundTaskIdentifier bgTask is instance variable
    NSAssert(self->bgTask == UIBackgroundTaskInvalid, nil);
    bgTask = [application beginBackgroundTaskWithExpirationHandler:
             ^{
                dispatch_async(dispatch_get_main_queue(),
                ^{
                    NSLog(@"stopping actions definitely");
                    [viewDiagCtrl stopBackGroundActivity:self];
                    [application endBackgroundTask:self->bgTask];
                    self->bgTask = UIBackgroundTaskInvalid;
                  });
              }];
    dispatch_async(dispatch_get_main_queue(),
    ^{
        // Start with 10ms time boxes
        NSTimeInterval  ti = 0.01;

        while ([application backgroundTimeRemaining] > 1.0)
        {
            if (![viewDiagCtrl.actionThread isExecuting])
                break;

            NSDate* date = [NSDate dateWithTimeIntervalSinceNow:ti];

            // Let the current run-loop do it's magif for one time-box.
            [[NSRunLoop currentRunLoop] runMode: NSRunLoopCommonModes
                         beforeDate: date];

            // Double the time box, for next try, max out at 1000ms.
            ti = MIN(1.0, ti * 2);
        }

        [viewDiagCtrl stopBackGroundActivity:self];
        [application endBackgroundTask:self->bgTask];
        self->bgTask = UIBackgroundTaskInvalid;
     });
}

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

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

1 Ответ

3 голосов
/ 19 марта 2011

Вы можете перенести свою работу в фоновый поток, например:

...

[self performSelectorInBackground:@selector(backgroundThread)
                       withObject:nil];

...


-(void)backgroundThread
{
    // do a download once a minute in a background thread - dont let the system suspend us 
    while(true)
    {
        BOOL expire = NO;    
        bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{expire = YES;}];
        while(!downloadComplete && !expire)
        {
             //do your multiple file downloads here;
        }
        [[UIApplication sharedApplication] endBackgroundTask:bgTask];

        [NSThread sleepForTimeInterval:60];
    }
}

Что это будет делать, так это продолжать выполнение задачи, которая будет продолжаться, когда приложение будет установлено в фоновом режиме - я делаю это в нескольких местах моего многопоточного / интенсивного сетевого приложения.

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

Я также использую NSURLRequest с:

[request setNetworkServiceType:NSURLNetworkServiceTypeVoIP];

(и использовать фоновый режим voip во флаге поддерживаемых фоновых режимов)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...