在iOS中创建一个持续的后台线程

4

我有一个要求,需要创建一个只在应用程序处于活动模式时运行的后台处理器。我已经尝试着制作了一个我想要实现的骨架,但是却无法使它工作。

我希望这个后台处理器在应用程序进入非活动状态时进入睡眠状态,并在应用程序恢复到活动模式时继续运行。我在下面提供了我的代码骨架。请问有人能帮我解决问题吗?

AppDelegate.h

#import <Foundation/Foundation.h>

@class BackgroundProcessor;

@interface AppDelegate_iPhone : UIResponder<UIApplicationDelegate>{
    BackgroundProcessor* processor;
}

@property(nonatomic) BackgroundProcessor *processor;

@end

AppDelegate.m

#import "AppDelegate_iPhone.h"
#import "BackgroundProcessor.h"


@implementation AppDelegate_iPhone

@synthesize processor;

-(BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    processor = [[BackgroundProcessor alloc]init];
    [processor Start];         
    return YES;
}

-(void) applicationDidEnterBackground:(UIApplication *)application
{
    [processor Sleep];
    NSLog(@"Entered Background"); 
}

-(void) applicationDidBecomeActive:(UIApplication *)application
{
    [processor Resume];
    NSLog(@"Became Active"); 
}
@end

BackgroundProcessor.h

#import <Foundation/Foundation.h>

@interface BackgroundProcessor : NSObject
{
    NSThread* processor;
}

@property (nonatomic) NSThread *processor;

-(void) Start;
-(void) Sleep;
-(void) workloop;
-(void) Resume;
@end

BackgroundProcessor.m

#import "BackgroundProcessor.h"

@implementation BackgroundProcessor
@synthesize processor;
-(id) init
{
    self = [super init];
    if(self)
    {
        processor = [[NSThread alloc] initWithTarget:self selector:@selector(workloop) object:nil];
    }
    return self;
}

-(void) Start
{
    if(processor)
        [processor start];
}

-(void) Sleep
{
    //    [processor 
    [NSThread sleepForTimeInterval: 0.1];
}

-(void) workloop
{
    NSLog(@"Background Processor Processing ....");  
    [NSThread sleepForTimeInterval:0.1];
}

- (void) Resume
{
    NSLog(@"Background Resuming ....");  
    [NSThread sleepForTimeInterval: 0.1];
}

我无法使工作循环连续运行。希望有人能帮助我解决为什么后台无法运行的问题。

在Joshua Smith的建议下尝试了这个方法

#import "BackgroundProcessor.h"

@implementation BackgroundProcessor

-(id) init
{
    self = [super init];
    if(self)
    {
        queue = [[NSOperationQueue alloc] init];
        NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(workloop) object:nil];
        [queue addOperation:operation];
    }
    return self;
}

-(void) workloop
{

    NSLog(@"Sleeping for 10 seconds");  
    sleep(10);
    NSLog(@"Background Processor Processing ....");  
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(workloop) object:nil];
    [queue addOperation:operation];
}

@end
5个回答

4

我同意Joshua的观点。通常情况下,你应该避免创建自己的线程。线程会占用物理内存和关键系统资源。GCD是一种更有效的管理并发执行的方式,而且使用起来更加容易。 - Duncan C
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperationQueue_class/Reference/Reference.html 是开始的最佳位置。在高层次上,您需要将作业放入队列中(以检查数据库),并在操作开始耗尽时重新填充队列。您可以使用GCD执行相同的操作。 - Joshua Smith
我喜欢你提出的方法,即不断填充队列以执行操作。这样做会有性能问题吗?你建议每次填充多少个操作? - Abishek
这方面没有特别的性能问题,使用操作的数量完全取决于它们被使用的速度、占用的内存等。优化队列填充可能需要一些微调(这是一个难题)。 - Joshua Smith
我会在你的循环中添加一个检查,以检查作业是否已被取消和/或队列本身是否已被取消(NSOperationQueue文档中有示例)。 - Joshua Smith
显示剩余3条评论

2

我已经实现了类似于您描述的方案。实现的方法是在后台线程上运行一个持续的定时器。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        notification = [[NotificationCentre alloc] init];

        notificationTimer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:@selector(notificationTimerFired:) userInfo:nil repeats:YES];

        [[NSRunLoop currentRunLoop] run]; 

    });
    [self.window makeKeyAndVisible];

    return YES;
}

-(void)notificationTimerFired:(NSTimer *)theTimer {
    BOOL hasNotifications;

    if (notificationTimerActive == YES) {

        do {
            hasNotifications = [notification GetNotifications];
        } while (hasNotifications == YES);

    }

    return;

}

基本上,当应用程序启动时,我会异步启动一个重复的计时器,然后调用我的notificationCenter类中的GetNotifications方法。


0

默认情况下,当您进入后台时,所有进程都将停止。只有少数不同的进程可以在后台执行。即使您创建了一个新线程,当您进入后台时它仍然会停止。


我希望这只在应用程序处于前台或活动状态时发生,而不是在后台/非活动状态下发生。 - Abishek
然后应用程序应该自动发生,如果您想确保,可以在进入后台时释放后台处理器,当应用程序再次变为活动状态时,您可以重新初始化对象。 - mlindy92

0

根据您的评论

我基本上想要生成一个线程,定期检查数据库以将数据从我的应用程序发送到服务器。您会推荐什么理想的选项来执行此类操作?是否有一篇文章或代码可以参考,以避免生成自己的线程?并且此操作仅需要在应用程序处于前台或活动模式时发生。

我认为您在这里使用了错误的方法。当应用程序处于前台时,只需定期启动异步操作以更新服务器即可。对于此用例,使用长时间运行的后台线程是完全错误的。


0
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [[NSRunLoop currentRunLoop] run];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接