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

标签 iphone multithreading user-interface

我已经深入研究了一个合适的解决方案,但它没有发挥作用: 我的 iPhone 应用程序根据用户需求通过 NSURL 请求更新数据。在线加载的每个文件大小仅为 1.5k,但一次更新可以包含 400 个此类文件。因此,我在一个可取消的单独线程中执行下载内容,并且在更新期间有一个带有进程指示和取消按钮的 UIAlertView。该事情可能会运行 1...3 分钟,因此它可能会超过设备保持事件状态的超时时间或发生其他事情,并且我的应用程序将进入后台。

当我在调用 applicationDidEnterBackground 时什么都不做时,我意识到我的应用程序和工作线程都被挂起。当应用程序再次处于前台时,它会唤醒并继续工作。到目前为止,一切都很好。当我再次进入前台后按下取消按钮时,我遇到了麻烦 - 然后线程 react 为被取消(理应如此),但立即再次运行并在最后崩溃(在 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;
     });
}

我对整个队列的内容没有经验,并且在某处找到了这样的构造。我假设所有在主线程中分派(dispatch)的工作都在工作线程中保持挂起状态,因此我没有任何机会取消它。有什么想法可以解决吗?

我还读过有关尝试在没有多线程的情况下完成所有操作的内容 - 但我不太欣赏这一点。是否有一些有用的链接可以正确处理“进入后台”情况?

最佳答案

您可以将线程工作包装在后台线程中,例如:

...

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

这将继续正在进行的任务,该任务将在应用程序后台运行时继续 - 我在我的重线程/网络密集型应用程序中的几个地方执行此操作。

据我所知:您不必等到应用程序进入后台才能调用 beginBackgroundTask - 只要您的应用程序有需要完成的功能而不被中断/挂起,您就应该调用它。

我还使用 NSURLRequest :

[request setNetworkServiceType:NSURLNetworkServiceTypeVoIP];

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

关于iphone - 如何在 iOS 上取消 applicationDidEnterBackground 中的工作线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5355479/

相关文章:

iPhone 服务器客户端应用程序

iphone - 预编译 prefix.pch 时出现奇怪的编译器警告

multithreading - 制作基本 OpenMP 类库的最简单方法

c - Windows在默认线程池中设置最大线程数

c++ - 什么 C+ +'s equivalent to winapi' s MsgWaitForMultipleObjectsEx

matlab - 在 matlab GUI 中创建选项卡

ios - 检测 UIWebView ScrollView 的 contentSize 的变化

iphone - 使用 tableview 作为 uitextview

iphone - 如何更改 AppleID 以检索配置文件

java - 我需要最简单的方法在 AWT 面板中用 java 画一条线