IOS 7 learning: multi-task processing-Background Fetch

Source: Internet
Author: User

In iOS7, Apple provides two APIs for developers to update application interfaces and content in the background. The first API is Background Fetch, which allows developers to perform specific actions, such as obtaining Network Content and updating program interfaces, after a periodic interval. The second API is Remote Notification, which is a new feature. It uses Push Notifications to notify programs when a new event occurs. These two new features are implemented in the background, which is more conducive to multi-task execution.

This article only describes Background Fetch ). (The certificate is complicated when sending Remote push, so the second item is not tried here)

A notable manifestation of multitasking is the background app switcher interface (which has been used in the iOS 6 jailbreak plug-in ), this interface displays a snapshot of all background programs when they exit the foreground. When the background work is completed, the developer can update the program snapshot to display the preview of the new content. For example, we can see the badgeNumber prompt, qq information prompt, and the latest weather prompt on the background Weibo. This allows you to preview the latest content without opening the application. Background Fetch is ideal for completing the preceding tasks.

 

Here is a Demo.

Step 1: configure the background mode for the program:

 

Step 2: set the time period of the program's Background Fetch:

 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{    [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];        return YES;}

Here, BackgroundFetchInterval can be set to two values:

 

 

UIKIT_EXTERN const NSTimeInterval UIApplicationBackgroundFetchIntervalMinimum NS_AVAILABLE_IOS(7_0);UIKIT_EXTERN const NSTimeInterval UIApplicationBackgroundFetchIntervalNever NS_AVAILABLE_IOS(7_0);

Among them, UIApplicationBackgroundFetchIntervalMinimum indicates that the system should manage as often as possible when the program is awakened and executes fetch tasks. If it is UIApplicationBackgroundFetchIntervalNever, our program will never be able to get programs in the background, of course, if our program completes a task and does not need to load data in the Background, use this value to disable the Background Fetch function.

 

If neither value is required, you can set an NSTimeInterval value here.

 

The next step is the key delegate method:

 

/// Applications with the fetch background mode may be given opportunities to fetch updated content in the background or when it is convenient for the system. This method will be called in these situations. You should call the fetchCompletionHandler as soon as you're finished performing that operation, so the system can accurately estimate its power and data cost.- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0);

 

After the system wakes up the background application, this delegate method will be executed. Note that you only have 30 seconds to determine whether the new content is available (in the objc. io iOS 7 Multitasking: Background Fetch and Remote Notification start to work 30 seconds before the application wakes up ), then process the new content and update the interface. 30 seconds should be enough to get data from the network and get the thumbnail of the interface, up to 30 seconds. The parameter completionHandler is a code block. After network requests and the update interface are completed, you should call this code block to complete the callback action.

When you execute completionHandler, the system will estimate the amount of power consumed by the program process and record whether new data is available based on the passed UIBackgroundFetchResult parameter. During the call process, the background snapshot of the application will be updated, and the corresponding app switcher will also be updated.

In actual applications, we should pass the completionHandler to the child components of the application or save it, and then call it after processing the data and updating the interface. In this Demo, I save the completionHandler in the Global Program delegate:

 

#import 
 
  typedef void (^CompletionHandler)(UIBackgroundFetchResult);@interface AppDelegate : UIResponder 
  
   @property (strong, nonatomic) UIWindow *window;+ (instancetype)sharedDelegate;@property (copy, nonatomic) CompletionHandler completionHandler;@end
  
 

The corresponding delegate method code is:

 

 

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(CompletionHandler)completionHandler {    NSLog(@Application Delegate: Perform Fetch);        UINavigationController *naviController = (UINavigationController *)self.window.rootViewController;    WebTableViewController *webTableController = (WebTableViewController *)naviController.topViewController;    self.completionHandler = completionHandler;    [webTableController updateBackgroundFetchResult];        application.applicationIconBadgeNumber += 1;}

 

Application. applicationIconBadgeNumber + = 1; indicates that when you receive a background fetch request, you will be prompted on the springboard. (I hate this thing very much and do not like it .)



WebTableController is a key part of the content displayed in this Demo. We can simulate the background fetch mode in debug. After capturing new data in the background, we will update the table in webTableController.

 

- (void)updateBackgroundFetchResult {    WebItem *item = [WebSimulator getNewWebItem];    [self.webContents insertObject:item atIndex:0];        NSMutableArray *updateContents = [NSMutableArray array];    [updateContents addObject:[NSIndexPath indexPathForItem:0 inSection:0]];    [self.tableView insertRowsAtIndexPaths:updateContents withRowAnimation:UITableViewRowAnimationFade];        AppDelegate *appDelegate = [AppDelegate sharedDelegate];    appDelegate.completionHandler = NULL;}


 

Here, I use a WebSimulator class to simulate obtaining data from the network, generating a random number each time, and then generating the corresponding URL to return. The method is as follows:

 

+ (WebItem *) getNewWebItem {unsigned int randomNumber = arc4random () % 4; NSMutableDictionary * webInfo = [NSMutableDictionary dictionary]; switch (randomNumber) {case 0: webInfo [TITLE_KEY] = BAIDU; webInfo [WEBURL_KEY] = BAIDU_URL; break; case 1: webInfo [TITLE_KEY] = MAIL_126; webInfo [WEBURL_KEY] = MAIL_126_URL; break; case 2: webInfo [TITLE_KEY] = SINA; webInfo [WEBURL_KEY] = SINA_URL; break; case 3: webInfo [TITLE_KEY] = SOGOU; webInfo [WEBURL_KEY] = SOGOU_URL; break; default: webInfo [TITLE_KEY] = BAIDU; webInfo [WEBURL_KEY] = BAIDU_URL; break;} NSLog (@ crawled Network Content: % @, webInfo [TITLE_KEY]); return [[WebItem alloc] initWithWebInfo: webInfo];}


 

 

In this case, you need to load the newly inserted cell in the table:

 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {    WebCell *cell = (WebCell *)[tableView dequeueReusableCellWithIdentifier:@CellIdentifier forIndexPath:indexPath];        WebItem *item = self.webContents[(NSUInteger)indexPath.row];        [cell configureCellWithWebItem:item];        return cell;}

ConfigureCellWithWebItem: The method is in the Custom WebCell class:

 

 

- (void)configureCellWithWebItem:(WebItem *)item {    self.showInfo_label.text = item.title;    [self showWebContent:item.webURL];}- (void)showWebContent:(NSURL *)url {    CompletionHandler handler = [AppDelegate sharedDelegate].completionHandler;    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];    NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:                                  ^(NSData *data, NSURLResponse *response, NSError *error) {                                      if (error) {                                          if (handler != NULL) {                                              handler(UIBackgroundFetchResultFailed);                                          }                                          return;                                      }                                                                            if (data && data.length > 0) {                                          dispatch_async(dispatch_get_main_queue(), ^{                                              [self.content_webView loadData:data MIMEType:nil textEncodingName:nil baseURL:nil];                                          });                                          if (handler != NULL) {                                              handler(UIBackgroundFetchResultNewData);                                          }                                      }                                      else {                                          if (handler != NULL) {                                              handler(UIBackgroundFetchResultNoData);                                          }                                      }                                  }];    [task resume];}

 

 

The preceding method starts a NSURLSession DataTask to load data in the URL generated by WebSimulator.

In the completionHandler code block of the Data Task, we determine whether the data Task is successfully executed based on error and Data or response, and then execute the completion handler of background fetch (through [AppDelegatesharedDelegate]. to update program snapshots. Note that the completion handler should be cleared in the appropriate place. Otherwise, the reloadData of the table will be affected (when callback is not required ).

 

To test the running result:

1. Run the program first. When the app is just started, there is only one row in the table. Then go to the background and perform the background fetch simulation:

 

2. You can see a prompt on the springboard program:

 

3. Open the app switcher and you will see that the app's snapshot has been updated:

 

4. Enter the program and you can see that the table is changed to two rows (only one row of new content is inserted in each background fetch). The console output is as follows:

 

03:20:48. 541 BackgroundFetch [5406: 70b] Application Delegate: Did Finish Lauching2014-02-13 03:20:48. 542 BackgroundFetch [5406: 70b] Launched in background 02014-02-13 03:20:48. 547 Network Content captured by BackgroundFetch [5406: 70b]: sogou 03:20:48. 611 BackgroundFetch [5406: 70b] Application Delegate: Did Become Active2014-02-13 03:20:53. 863 BackgroundFetch [5406: 70b] Application Delegate: Will Resign Active2014-02-13 03:20:53. 865 BackgroundFetch [5406: 70b] Application Delegate: Did Enter Background2014-02-13 03:20:59. 130 BackgroundFetch [5406: 70b] Application Delegate: Perform Fetch2014-02-13 03:20:59. 130 BackgroundFetch [5406: 70b] the captured Network Content: Baidu 03:20:59. 342 BackgroundFetch [5406: 6a33] background capture results: UIBackgroundFetchResultNewData2014-02-13 03:27:22. 843 BackgroundFetch [5406: 70b] Application Delegate: Will Enter Foreground2014-02-13 03:27:22. 845 BackgroundFetch [5406: 70b] Application Delegate: Did Become Active

From Lauched in background 0, we can see that the program runs before and is not started from the background.

 

After app did enter background, we perform background fetch. At this time, the app in the background will be awakened, and the perform fetch method in the delegate will be executed. After the background capture task is executed, completion handler is executed.

 

5. in addition, you can set it to another Startup Mode: The program is not running (including not running in the background), after a certain period (similar to a timer) the program will be awakened by the system and started in the background, which can be changed in scheme:

 

Double-click one of the scheme opened (of course, you can create another scheme and set it to the background Startup Mode). The settings are as follows:

 

Then start the program, and the console outputs:

 

03:40:21. 499 BackgroundFetch [5594: 70b] Application Delegate: Did Finish Lauching2014-02-13 03:40:21. 500 BackgroundFetch [5594: 70b] Launched in background 12014-02-13 03:40:21. 505 BackgroundFetch [5594: 70b] the captured Network Content: Sina 03:40:21. 573 BackgroundFetch [5594: 70b] Application Delegate: Perform Fetch2014-02-13 03:40:21. 573 BackgroundFetch [5594: 70b] the captured Network Content: Baidu 03:40:21. 769 BackgroundFetch [5594: 4d03] background capture result: UIBackgroundFetchResultNewData

You can see that the program is started from the background: Lauched in background 1, and the background fetch operation is performed as soon as it is started. The springboard app also receives a prompt:

 

 

Of course, app switcher is also updated.

 

We can see that the biggest benefit of background fetch is that it does not require manual participation in data acquisition. For example, we need to refresh it manually when we want to watch Weibo, with the background fetch, the app will regularly refresh Weibo to ensure that the latest and most timely information is displayed each time we open the app, which is undoubtedly very suitable for social networking applications and weather applications. The disadvantage is traffic.

 

Note that when running the Demo, the following error occurs if the background fetch interval is too short:

 

2014-02-13 03:50:57.602 BackgroundFetch[5649:7513] bool _WebTryThreadLock(bool), 0xa173900: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...1   0x514a1ae WebThreadLock2   0x44c3a7 -[UIWebDocumentView setFrame:]3   0x6d6106 -[UIWebBrowserView setFrame:]4   0x44fd5e -[UIWebDocumentView _resetForNewPage]5   0x450acf -[UIWebDocumentView layoutSubviews]6   0x299267 -[UIView(CALayerDelegate) layoutSublayersOfLayer:]7   0x14d281f -[NSObject performSelector:withObject:]8   0x3b4b2ea -[CALayer layoutSublayers]9   0x3b3f0d4 CA::Layer::layout_if_needed(CA::Transaction*)10  0x3b3ef40 CA::Layer::layout_and_display_if_needed(CA::Transaction*)11  0x3aa6ae6 CA::Context::commit_transaction(CA::Transaction*)12  0x3aa7e71 CA::Transaction::commit()13  0x3b64430 +[CATransaction flush]14  0x26a296 _UIWindowUpdateVisibleContextOrder15  0x26a145 +[UIWindow _prepareWindowsPassingTestForAppResume:]16  0x23f016 -[UIApplication _updateSnapshotAndStateRestorationArchiveForBackgroundEvent:saveState:exitIfCouldNotRestoreState:]17  0x23f390 -[UIApplication _replyToBackgroundFetchRequestWithResult:remoteNotificationToken:sequenceNumber:updateApplicationSnapshot:]18  0x23fbb6 __61-[UIApplication _handleOpportunisticFetchWithSequenceNumber:]_block_invoke19  0x682a04 ___UIAutologgingBackgroundFetchBlock_block_invoke20  0x3d6d __26-[WebCell showWebContent:]_block_invoke21  0x61d2195 __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke22  0x625f286 __37-[__NSCFURLSession addDelegateBlock:]_block_invoke23  0x113c945 -[NSBlockOperation main]24  0x1195829 -[__NSOperationInternal _start:]25  0x1112558 -[NSOperation start]26  0x1197af4 __NSOQSchedule_f27  0x1ae94b0 _dispatch_client_callout28  0x1ad707f _dispatch_queue_drain29  0x1ad6e7a _dispatch_queue_invoke30  0x1ad7e1f _dispatch_root_queue_drain31  0x1ad8137 _dispatch_worker_thread2

It seems that webview is forced to execute a new task before it completes the data loading task, so it cannot obtain the thread lock. Of course, in actual applications, apps cannot be retrieved in the background frequently (Apple cannot ).

 

This problem has not been solved yet. Please give me some advice. So I modified it as follows:

 

- (void)updateBackgroundFetchResult {    WebItem *item = [WebSimulator getNewWebItem];    [self.webContents insertObject:item atIndex:0];        NSMutableArray *updateContents = [NSMutableArray array];    [updateContents addObject:[NSIndexPath indexPathForItem:0 inSection:0]];    [self.tableView insertRowsAtIndexPaths:updateContents withRowAnimation:UITableViewRowAnimationFade];        AppDelegate *appDelegate = [AppDelegate sharedDelegate];    if (appDelegate.completionHandler != NULL) {        CompletionHandler handler = appDelegate.completionHandler;        handler(UIBackgroundFetchResultNewData);        appDelegate.completionHandler = NULL;    }}

-(Void) showWebContent :( NSURL *) url {// CompletionHandler handler = [AppDelegate sharedDelegate]. completionHandler; optional * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession * session = [NSURLSession sessionWithConfiguration: sessionConfiguration]; optional * task = [session completion: url completionHandler: ^ (NSData * data, NSURLResponse * response, NSError * error) {if (error) {// if (handler! = NULL) {// handler (UIBackgroundFetchResultFailed); // NSLog (@ background capture result: UIBackgroundFetchResultFailed); //} return;} if (data & data. length> 0) {dispatch_async (dispatch_get_main_queue (), ^ {[self. content_webView loadData: data MIMEType: nil textEncodingName: nil baseURL: nil] ;}); // if (handler! = NULL) {// handler (UIBackgroundFetchResultNewData); // NSLog (@ background capture result: UIBackgroundFetchResultNewData); //} else {// if (handler! = NULL) {// handler (UIBackgroundFetchResultNoData); // NSLog (@ background capture result: UIBackgroundFetchResultNoData); // }}]; [task resume];}

No errors have been tested.

 

 

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.