1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. Kỹ thuật lập trình >

2  Completing a Long-Running Task in the Background

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



Xem Thêm
Tải bản đầy đủ (.pdf) (900 trang)

×