Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (24.65 MB, 900 trang )
An example could be an application that is calling a web service API and has not yet
received the response of that API from the server. During this time, if the application
is sent to the background, the application can request more time until it receives a
response from the server. Once the response is received, the application must save its
state and mark that task as finished by calling the endBackgroundTask: instance method
of UIApplication.
Let's have a look at an example. I will start by defining a property of type UIBackground
TaskIdentifier in the app delegate. Also, let's define a timer of type NSTimer which we
will use to print a message to the console window every 1 second when our app is sent
to the background:
#import
@interface Completing_a_Long_Running_Task_in_the_BackgroundAppDelegate
: UIResponder
@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, unsafe_unretained)
UIBackgroundTaskIdentifier backgroundTaskIdentifier;
@property (nonatomic, strong) NSTimer *myTimer;
@end
And then we will continue on to synthesize our properties:
#import "Completing_a_Long_Running_Task_in_the_BackgroundAppDelegate.h"
@implementation Completing_a_Long_Running_Task_in_the_BackgroundAppDelegate
@synthesize window = _window;
@synthesize backgroundTaskIdentifier;
@synthesize myTimer;
...
Now let's move on to creating and scheduling our timer when the app gets sent to the
background:
- (BOOL) isMultitaskingSupported{
BOOL result = NO;
if ([[UIDevice currentDevice]
respondsToSelector:@selector(isMultitaskingSupported)]){
result = [[UIDevice currentDevice] isMultitaskingSupported];
}
return result;
}
- (void) timerMethod:(NSTimer *)paramSender{
618 | Chapter 12: Multitasking
www.it-ebooks.info
NSTimeInterval backgroundTimeRemaining =
[[UIApplication sharedApplication] backgroundTimeRemaining];
if (backgroundTimeRemaining == DBL_MAX){
NSLog(@"Background Time Remaining = Undetermined");
} else {
NSLog(@"Background Time Remaining = %.02f Seconds",
backgroundTimeRemaining);
}
}
- (void)applicationDidEnterBackground:(UIApplication *)application{
if ([self isMultitaskingSupported] == NO){
return;
}
self.myTimer =
[NSTimer scheduledTimerWithTimeInterval:1.0f
target:self
selector:@selector(timerMethod:)
userInfo:nil
repeats:YES];
self.backgroundTaskIdentifier =
[application beginBackgroundTaskWithExpirationHandler:^(void) {
[self endBackgroundTask];
}];
}
You can see that in the completion handler for our background task, we are calling the
endBackgroundTask method of our app delegate. This is a method which we have written
and it looks like this:
- (void) endBackgroundTask{
dispatch_queue_t mainQueue = dispatch_get_main_queue();
__weak Completing_a_Long_Running_Task_in_the_BackgroundAppDelegate
*weakSelf = self;
dispatch_async(mainQueue, ^(void) {
Completing_a_Long_Running_Task_in_the_BackgroundAppDelegate
*strongSelf = weakSelf;
if (strongSelf != nil){
[strongSelf.myTimer invalidate];
[[UIApplication sharedApplication]
endBackgroundTask:self.backgroundTaskIdentifier];
strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
12.2 Completing a Long-Running Task in the Background | 619
www.it-ebooks.info
});
}
There are a couple of things that we need to do to clear up after a long running task:
1. End any threads or timers, whether they are foundation timers or they are created
with GCD.
2. End the background task by calling the endBackgroundTask: method of UIApplica
tion.
3. Mark our task as ended by assigning the value of UIBackgroundTaskInvalid to our
task identifiers.
Last but not least, when our app is brought to the foreground, if we still have our
background task running, we need to ensure that we are getting rid of it:
- (void)applicationWillEnterForeground:(UIApplication *)application{
if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid){
[self endBackgroundTask];
}
}
In our example, whenever the application is put into the background, we ask for more
time to finish a long-running task (in this case, for instance, our timer's code). In our
time, we constantly read the value of the backgroundTimeRemaining property of UIAppli
cation’s instance and print that value out to the console. In the beginBackgroundTask
WithExpirationHandler: instance method of UIApplication, we provided the code that
will be executed just before our application’s extra time to execute a long-running task
finishes (usually about 5 to 10 seconds before the expiration of the task). In here, we
can simply end the task by calling the endBackgroundTask: instance method of UIAppli
cation.
When an application is sent to the background and the application has
requested more execution time from iOS, before the execution time is
finished, the application could be revived and brought to the foreground
by the user again. If you had previously asked for a long-running task
to be executed in the background when the application was being sent
to the background, you must end the long-running task using the end
BackgroundTask: instance method of UIApplication.
See Also
Recipe 12.1
620 | Chapter 12: Multitasking
www.it-ebooks.info
12.3 Receiving Local Notifications in the Background
Problem
You want to present an alert to your user even when your application is not running.
You want to create this alert locally inside your application without using push
notifications.
Solution
Instantiate an object of type UILocalNotification and schedule it using the schedule
LocalNotification: instance method of UIApplication.
- (BOOL) localNotificationWithMessage:(NSString *)paramMessage
actionButtonTitle:(NSString *)paramActionButtonTitle
launchImage:(NSString *)paramLaunchImage
applicationBadge:(NSInteger)paramApplicationBadge
secondsFromNow:(NSTimeInterval)paramSecondsFromNow
userInfo:(NSDictionary *)paramUserInfo{
if ([paramMessage length] == 0){
return NO;
}
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = paramMessage;
notification.alertAction = paramActionButtonTitle;
if ([paramActionButtonTitle length]> 0){
/* Make sure we have the action button for the user to press
to open our application */
notification.hasAction = YES;
} else {
notification.hasAction = NO;
}
/* Here you have a chance to change the launch image of your application
when the notification's action is viewed by the user */
notification.alertLaunchImage = paramLaunchImage;
/* Change the badge number of the application once the notification is
presented to the user. Even if the user dismisses the notification,
the badge number of the application will change */
notification.applicationIconBadgeNumber = paramApplicationBadge;
/* This dictionary will get passed to your application
later if and when the user decides to view this notification */
notification.userInfo = paramUserInfo;
/* We need to get the system time zone so that the alert view
will adjust its fire date if the user's time zone changes */
NSTimeZone *timeZone = [NSTimeZone systemTimeZone];
notification.timeZone = timeZone;
12.3 Receiving Local Notifications in the Background | 621
www.it-ebooks.info
/* Schedule the delivery of this notification 10 seconds from now */
NSDate *today = [NSDate date];
NSDate *fireDate = [today dateByAddingTimeInterval:paramSecondsFromNow];
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSUInteger dateComponents =
NSYearCalendarUnit |
NSMonthCalendarUnit |
NSDayCalendarUnit |
NSHourCalendarUnit |
NSMinuteCalendarUnit |
NSSecondCalendarUnit;
NSDateComponents *components = [calendar components:dateComponents
fromDate:fireDate];
/* Here you have a chance to change these components. That's why we
retrieved the components of the date in the first place. */
fireDate = [calendar dateFromComponents:components];
/* Finally set the schedule date for this notification */
notification.fireDate = fireDate;
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
return YES;
}
Discussion
A local notification is an alert view (an object of type UIAlertView) that gets presented
to the user if your application is running in the background or not running at all. You
can schedule the delivery of a local notification using the scheduleLocalNotifica
tion: instance method of UIApplication. The cancelAllLocalNotifications instance
method cancels the delivery of all pending local notifications.
You can ask iOS to deliver a local notification to the user in the future when your
application is not even running. These notifications could also be recurring—for instance, every week at a certain time. However, extra care must be taken when you are
specifying the fire date for your notifications.
For instance, let’s say the time is now 13:00 in London, the time zone is GMT+0, and
your application is currently running on a user’s device. You want to be able to deliver
a notification at 14:00 to your user even if your application is not running at that time.
Now your user is on a plane at London’s Gatwick Airport and plans to fly to Stockholm
where the time zone is GMT+1. If the flight takes 30 minutes, the user will be in Stock-
622 | Chapter 12: Multitasking
www.it-ebooks.info