Google+

Tuesday, January 13, 2015

Background download iOS

1. Setup background download session 


To setup a background download session in iOS is quite straightforward. Firstly setup a configuration, and use this configuration to declare a NSURLSession.

NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];
_downloadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];

Then we declare a NSURLSessionDownloadTask, and start this task by calling resume:
NSURLSessionDownloadTask *task = [fh.downloadSession downloadTaskWithURL:aNSURL];
[task resume];

After that, one should also add four delegate methods which are:
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
}
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
}
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
}
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
}

One could collect the download results in the third method "didFinishDownloadingToURL" by using:
 
NSData *aData = [NSData dataWithContentsOfURL:location];

Notice that the fourth method "didCompleteWithError" will always be called. If the task is finished without error, it returns nil. Otherwise, it returns some error code for us to catch the error.

The task started in foreground will continue its downloading after entering to background.

2. Batch background download data from MusicBrainz 


MusicBrainz is a music meta-information database, one can find and download these information by searching song title, artist name, or album title, etc. It offers a public API for developers to query its database.

But there is a query/request rate limit which is about 1 request/second. That is to say if we launch, say , 100 request tasks at the same time, the server will reject them all. To achieve a successful query without being rejected, I schedule the tasks in a queue by "dispatch_after":

for (MPMediaItem *item in songLibrary) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // start background music meta-data download task here.
    });
}

The problem arises when we enter to background, even our download task support background download mode, but our dispatch_after block will stop running. To deal with this problem, I manage to ask for more running time after the app entering to background:

- (void)applicationDidEnterBackground:(UIApplication *)application {
    _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
    [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
    _backgroundTask = UIBackgroundTaskInvalid;
    }];
}

The method "beginBackgroundTaskWithExpirationHandler" will be called when our _backgroundTask remaining time expired. In my iPhone 4, the system leaves 178 seconds for background task executing.

This method is far from good and needs to be improved.

No comments:

Post a Comment