Crystal
Crystal

Reputation: 29518

Using multiple beginBackgroundTaskWithExpirationHandler calls

I am trying to follow this previous post here: Best practice to send a lot of data in background on iOS4 device?

And basically, I have a method called getRequest that grabs information from the web server. There are about 50 pieces of data I need from the web server. So at the same time, I have 50 delegate calls to connectionDidFinishLoading. Currently my getRequest looks like:

-(void) getRequestWithURL:(NSString *) requestURL 
{
    static int getRequest = 0;
    NSLog(@"getRequest: %i", getRequest);
    getRequest++;

    UIApplication *app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier taskID;
    taskID = [app beginBackgroundTaskWithExpirationHandler:^{

        NSLog(@"Time remaining: %f", app.backgroundTimeRemaining);

        NSLog(@"Background task not completed");
        [app endBackgroundTask:taskID];
    }];

    NSURLRequest *req = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:requestURL]];
    NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:req delegate:self] ; 
    [self startRequestWithConnection:con];
    [req release];

    if (taskID == UIBackgroundTaskInvalid) {
        NSLog(@"Failed to create background task identifier");
    }

}

Then in my connectionDidFinishLoading:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
        // process data from server
   // endBackgroundTask:someTaskID???
}

I know you are allowed to have multiple calls of beginBackgroundTaskWithExpirationHandler, but I don't know if what I'm doing in my getRequest method is doing that since I only have one variable __block UIBackgroundTaskIdentifier taskID each time the method is called. And I'm also not sure if I need to call endBackgroundTask in the connectionDidFinishLoading method for each call to getRequest since you are supposed to balance the beginBackgroundTaskWithExpirationHandler with an endBackgroundTask: call. If so, how do I do that since my getRequest doesn't currently have that infrastructure? Do I need 50 ivars in order for the connectionDidFinishLoading method to see the 50 initial calls to getRequest? Thanks.

Upvotes: 2

Views: 5588

Answers (2)

Daniel O
Daniel O

Reputation: 2850

The work being done is simple whatever code comes next. The work isn't wrapped up into the Background task. The background task is just an id and a status that tells the iOS framework if you are finished doing your task or not. It's up to

Upvotes: 1

tim
tim

Reputation: 1682

As you said, you need to balance beginBackgroundTaskWithExpirationHandler call with an endBackgroundTask call.

One solution I have in mind looks like this:

Create a new instance variable

UIBackgroundTaskIdentifier backgroundTaskID;

You are counting the requests anyway so you could also decrement getRequest in connectionDidFinishLoading:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // process data from server
    getRequest--;

    if (getRequest == 0 && backgroundTaskID != UIBackgroundTaskInvalid)
    {
        [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
        backgroundTaskID = UIBackgroundTaskInvalid;
    }
}

Now the background task gets ended after the last request has been completed. To start only one background task you start it in a method that gets called when the app goes to the background.

You need to listen for the UIApplicationDidEnterBackgroundNotification

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(applicationDidEnterBackground)
                                             name:UIApplicationDidEnterBackgroundNotification
                                           object:nil];

and implement the method

- (void)applicationDidEnterBackground:(NSNotification *)notification
{
    if (getRequest > 0) {
        backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 
            [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
            backgroundTaskID = UIBackgroundTaskInvalid;
         }];
    }
}

Now you only have one running background task that starts automatically when your app goes to the background and you have running requests that gets ended when all your requests are done.

Another improvement would be to add your network requests to an NSOperationQueue to avoid the manual counting and limit the number of concurrent requests.

Upvotes: 5

Related Questions