如何在iOS上的applicationDidEnterBackground中取消工作线程

时间:2011-03-18 17:10:52

标签: iphone multithreading user-interface

我已经为一个合适的解决方案进行了大量的研究,但它没有发挥作用: 我的iPhone应用程序根据用户需求通过NSURL请求更新数据。在线加载的每个文件只有1.5k大小,但一个更新可以包含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 个答案:

答案 0 :(得分:3)

你可以将你的线程包装在后台线程中,如:

...

[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];

(并在supportedbackgroundmodes标志中使用voip后台模式)