1. Setup background download session
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