16.0~16.8 多任务 后台运行 Multitasking

来源:互联网 发布:cepii数据库 编辑:程序博客网 时间:2024/05/16 00:55

16.0. IntroductionMultitasking

多任务允许后台运行,意思是应用程序可以继续工作——跑任务,创建新线程,监听通知,事件处理——不过,就没法展示任何界面信息了,也没法与用户交互了。

当用户按下home键,在以前的版本中iPhone和iPad将停止应用程序的运行,而现在应用程序将进入后台。

当应用进入后台或回到前台,系统会发送不同的消息,它希望你的程序代理能处理这些消息,比如进入后台代理将收到applicationDidEnter Background:消息,回到前台将收到applicationWillEnterForeground:消息。

此外,iOS还发送其他消息给应用,当进入后台时发送UIApplicationDidEnterBackgroundNotification回到前台时发送UIApplicationWillEnterForegroundNotification.如果需要处理这些消息,需要先到通知中心注册。


16.1. Detecting the Availability of Multitasking

检查是否支持多任务

-(void)testMultitaskingSupported

{

    if ([selfisMultitaskingSupported]){

        NSLog(@"Multitasking is supported.");

    } else {

        NSLog(@"Multitasking is not supported.");

    }

}

- (BOOL) isMultitaskingSupported{

    BOOL result = NO;

    if ([[UIDevicecurrentDevice]

         respondsToSelector:@selector(isMultitaskingSupported)]){

        result = [[UIDevicecurrentDevice]isMultitaskingSupported];

    }

    return result;

}


打印:


2014-07-09 16:28:51.288 cookbook7_12[2767:907] Multitasking is supported.



16.2. Completing a Long-Running Task in the Background

进入后台后还想再执行一段时间


当应用进入后台后,主线程挂起,detachNewThreadSelec tor:toTarget:withObject:创建的子线程也挂起,如果你希望在进入后台后能再执行一会儿时间把还没完成的事做完,你必须调用beginBackgroundTaskWithExpirationHandler:方法给iOS多要点时间。backgroundTimeRemaining属性将会告诉你,你还有多长时间,如果你在这个时间内没结束,那个iOS将强制把你的应用终止掉。

beginBackgroundTaskWithExpirationHandler:方法必须有对应的endBackgroundTask:方法的调用,也就是说你应该告诉iOS你的工作结束了,这样应用就会进入后台,所有线程挂起。

#import <UIKit/UIKit.h>


@interface AppDelegate :UIResponder <UIApplicationDelegate>


@property (strong,nonatomic)UIWindow *window;


@property (nonatomic,unsafe_unretained)UIBackgroundTaskIdentifier backgroundTaskIdentifier;

@property (nonatomic,strong)NSTimer *myTimer;


@end



#import "AppDelegate.h"


@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    NSLog(@"%s",__FUNCTION__);

    // Override point for customization after application launch.

    

    return YES;

}

- (void)applicationWillResignActive:(UIApplication *)application

{

    NSLog(@"%s",__FUNCTION__);

    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.

    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.

}


- (void)applicationDidEnterBackground:(UIApplication *)application

{

    NSLog(@"%s",__FUNCTION__);

    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 

    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    if ([selfisMultitaskingSupported] ==NO){

        return;

    }

    self.myTimer =

    [NSTimerscheduledTimerWithTimeInterval:1.0f

                                    target:self

                                  selector:@selector(timerMethod:)

                                  userInfo:nil

                                   repeats:YES];

    self.backgroundTaskIdentifier =

    [application beginBackgroundTaskWithExpirationHandler:^(void) {

        [selfendBackgroundTask];

    }];

}


- (void)applicationWillEnterForeground:(UIApplication *)application

{

    NSLog(@"%s",__FUNCTION__);

    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.

    if (self.backgroundTaskIdentifier !=UIBackgroundTaskInvalid){

        [selfendBackgroundTask];

    }

}


- (void)applicationDidBecomeActive:(UIApplication *)application

{

    NSLog(@"%s",__FUNCTION__);

    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

}


- (void)applicationWillTerminate:(UIApplication *)application

{

    NSLog(@"%s",__FUNCTION__);

    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

}


- (BOOL) isMultitaskingSupported{

    NSLog(@"%s",__FUNCTION__);

    BOOL result = NO;

    if ([[UIDevicecurrentDevice]

         respondsToSelector:@selector(isMultitaskingSupported)]){

        result = [[UIDevicecurrentDevice]isMultitaskingSupported];

    }

    return result;

}


- (void) timerMethod:(NSTimer *)paramSender{

    NSLog(@"%s",__FUNCTION__);

    

    NSTimeInterval backgroundTimeRemaining =[[UIApplicationsharedApplication]backgroundTimeRemaining];

    if (backgroundTimeRemaining == DBL_MAX){

        NSLog(@"Background Time Remaining = Undetermined");

    } else {

        NSLog(@"Background Time Remaining = %.02f Seconds",backgroundTimeRemaining);

    }

}


- (void) endBackgroundTask{

    NSLog(@"%s",__FUNCTION__);

    

    dispatch_queue_t mainQueue =dispatch_get_main_queue();

    __weak AppDelegate *weakSelf = self;

    dispatch_async(mainQueue, ^(void) {

        AppDelegate *strongSelf = weakSelf;

        if (strongSelf != nil){

            [strongSelf.myTimerinvalidate];

            [[UIApplicationsharedApplication]endBackgroundTask:self.backgroundTaskIdentifier];

            strongSelf.backgroundTaskIdentifier =UIBackgroundTaskInvalid;

        }

    });

}


@end



运行后,home键进入后台,点击应用回到前台,然后。。。等。。。。

2014-07-10 11:14:59.310 cookbook7_12[506:c07] -[AppDelegate application:didFinishLaunchingWithOptions:]

2014-07-10 11:14:59.324 cookbook7_12[506:c07] -[AppDelegate applicationDidBecomeActive:]

2014-07-10 11:15:17.592 cookbook7_12[506:c07] -[AppDelegate applicationWillResignActive:]

2014-07-10 11:15:17.594 cookbook7_12[506:c07] -[AppDelegate applicationDidEnterBackground:]

2014-07-10 11:15:17.595 cookbook7_12[506:c07] -[AppDelegate isMultitaskingSupported]

2014-07-10 11:15:18.596 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:15:18.598 cookbook7_12[506:c07] Background Time Remaining = 598.99 Seconds

2014-07-10 11:15:19.596 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:15:19.597 cookbook7_12[506:c07] Background Time Remaining = 598.00 Seconds

2014-07-10 11:15:20.596 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:15:20.597 cookbook7_12[506:c07] Background Time Remaining = 597.00 Seconds


。。。。。。


2014-07-10 11:15:41.596 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:15:41.598 cookbook7_12[506:c07] Background Time Remaining = 575.99 Seconds

2014-07-10 11:15:42.597 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:15:42.599 cookbook7_12[506:c07] Background Time Remaining = 574.99 Seconds

2014-07-10 11:15:43.597 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:15:43.599 cookbook7_12[506:c07] Background Time Remaining = 573.99 Seconds

2014-07-10 11:15:44.044 cookbook7_12[506:c07] -[AppDelegate applicationWillEnterForeground:]

2014-07-10 11:15:44.045 cookbook7_12[506:c07] -[AppDelegate endBackgroundTask]

2014-07-10 11:15:44.046 cookbook7_12[506:c07] -[AppDelegate applicationDidBecomeActive:]

2014-07-10 11:15:53.936 cookbook7_12[506:c07] -[AppDelegate applicationWillResignActive:]

2014-07-10 11:15:53.937 cookbook7_12[506:c07] -[AppDelegate applicationDidEnterBackground:]

2014-07-10 11:15:53.938 cookbook7_12[506:c07] -[AppDelegate isMultitaskingSupported]

2014-07-10 11:15:54.939 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:15:54.941 cookbook7_12[506:c07] Background Time Remaining = 598.99 Seconds

2014-07-10 11:15:55.940 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:15:55.942 cookbook7_12[506:c07] Background Time Remaining = 597.99 Seconds


。。。。。。


2014-07-10 11:25:44.947 cookbook7_12[506:c07] Background Time Remaining = 8.99 Seconds

2014-07-10 11:25:45.946 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:25:45.948 cookbook7_12[506:c07] Background Time Remaining = 7.99 Seconds

2014-07-10 11:25:46.946 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:25:46.948 cookbook7_12[506:c07] Background Time Remaining = 6.99 Seconds

2014-07-10 11:25:47.946 cookbook7_12[506:c07] -[AppDelegate timerMethod:]

2014-07-10 11:25:47.948 cookbook7_12[506:c07] Background Time Remaining = 5.99 Seconds

2014-07-10 11:25:48.943 cookbook7_12[506:c07] -[AppDelegate endBackgroundTask]


当进入后台时,你给iOS申请时间了,在这段时间内,应用可能重新回到前台,这时你应该吧申请时间来执行的任务结束掉。(什么?我也不大懂)



16.3. Adding Background Fetch Capabilities to Your Apps

iOSSDK引进了新的功能是你能够在进入后台后仍能获取信息

很多程序是需要连接服务器的,获取数据,或提交数据等。进入后台后仍能获取信息的唯一方法就是给iOS申请时间,如上一节所说的那样。那是主动的方式,还有被动的方式,这种方式是,你程序坐那不动,iOS主动给你些时间让你在后台处理些信息。所以你要做的是开启这项功能然后等着iOS来唤醒。

比如你需要下载一些内容,如twitter应用,你希望在你打开应用的时候,信息是最新的。到目前为止,唯一的方法是当你打开的时候让应用去重新刷新下数据。但现在,iOS可以唤醒后台的twitter并让他去更新,这样每次你打开的是否信息自然是新的。

如何开启这项功能呢?project serring --> Capabilities tab --> background modes --> 勾选 backgroundfetch 

有两种方式的后台获取,一是应用在后台,iOS唤醒他,让他去获取数据,二是,应用未启动,iOS启动它(在后台)让他去获取。

iOS如何知道哪个应用需要唤醒,哪个不需要呢,这就得看你的程序了。

方法是调用UIApplication 的setMinimumBackgroundFetchInterval:方法。参数即是告诉iOS唤醒的时间间隔和频率。这个属性的默认值是UIApplicationBackgroundFetchIntervalNever,即是不唤醒。

AppDelegate.h文件

#import <UIKit/UIKit.h>


@interface AppDelegate :UIResponder <UIApplicationDelegate>


@property (strong,nonatomic)UIWindow *window;

@property (nonatomic,strong)NSMutableArray *allNewsItems;


@end




AppDelegate.m文件

#import "AppDelegate.h"

#import "NewsItem.h"



#pragma mark - AppDelegate

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    // Override point for customization after application launch.

    //设置唤醒时间  NS_AVAILABLE_IOS(7_0);

    [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];

    return YES;

}

- (void)applicationWillResignActive:(UIApplication *)application

{


}


- (void)applicationDidEnterBackground:(UIApplication *)application

{


}


- (void)applicationWillEnterForeground:(UIApplication *)application

{


}


- (void)applicationDidBecomeActive:(UIApplication *)application

{


}


- (void)applicationWillTerminate:(UIApplication *)application

{

    

}


//懒加载

- (NSMutableArray *) allNewsItems{

    if (_allNewsItems ==nil){

        _allNewsItems = [[NSMutableArray alloc] init];

        /* Pre-populate the array with one item */

        NewsItem *item = [[NewsItem alloc] init];

        item.date = [NSDatedate];

        item.text = [NSStringstringWithFormat:@"News text 1"];

        [_allNewsItemsaddObject:item];

    }

    return_allNewsItems;

}



//模拟服务端返回数据,有时有新数据,有时没有

- (void) fetchNewsItems:(BOOL *)paramFetchedNewItems{

    NSLog(@"%s",__FUNCTION__);

    if (arc4random_uniform(2) !=1){

        if (paramFetchedNewItems != nil){

            *paramFetchedNewItems =NO;

        }

        return;

    }

    [selfwillChangeValueForKey:@"allNewsItems"];/* Generate a new item */

    NewsItem *item = [[NewsItem alloc] init];

    item.date = [NSDatedate];

    item.text = [NSStringstringWithFormat:@"News text %lu",

                 (unsignedlong)self.allNewsItems.count +1];

    [self.allNewsItemsaddObject:item];

    NSLog(@"self.allNewsItems.cout=%d",self.allNewsItems.count);

    if (paramFetchedNewItems != nil){

        *paramFetchedNewItems =YES;

    }

    [selfdidChangeValueForKey:@"allNewsItems"];

}


//实现这个方法,当iOS让你去获取数据时就是调用这个方法的

//方法中的第二个参数是一个block,当获取完数据后需要执行这个block来返回获取的结果

//typedef NS_ENUM(NSUInteger, UIBackgroundFetchResult) {

//    UIBackgroundFetchResultNewData,

//    UIBackgroundFetchResultNoData,

//    UIBackgroundFetchResultFailed

//} NS_ENUM_AVAILABLE_IOS(7_0);

//iOS会根据获取的结果适当的调整唤醒的时间策略,这可以省电,不是吗?

- (void) application:(UIApplication *)application

performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))

completionHandler{

    NSLog(@"%s",__FUNCTION__);

    BOOL haveNewContent = NO;

    [selffetchNewsItems:&haveNewContent];

    if (haveNewContent){

        completionHandler(UIBackgroundFetchResultNewData);

    } else {

        completionHandler(UIBackgroundFetchResultNoData);

    }

}



@end



TableViewController.h文件


#import <UIKit/UIKit.h>


@interface TableViewController :UITableViewController


@end


TableViewController.m文件


#import "TableViewController.h"

#import "AppDelegate.h"

#import "NewsItem.h"


@interface TableViewController ()


@property (nonatomic,weak)NSArray *allNewsItems;

@property (nonatomic,unsafe_unretained)BOOL mustReloadView;


@end


@implementation TableViewController


- (id)initWithStyle:(UITableViewStyle)style

{

    self = [super initWithStyle:style];

    if (self) {

        // Custom initialization

    }

    return self;

}


- (void)viewDidLoad

{

    NSLog(@"%s",__FUNCTION__);

    [superviewDidLoad];


    AppDelegate *appDelegate = [UIApplicationsharedApplication].delegate;

    self.allNewsItems = appDelegate.allNewsItems;

    [appDelegate addObserver:self

                 forKeyPath:@"allNewsItems"

                     options:NSKeyValueObservingOptionNew

                    context:NULL];

    [[NSNotificationCenterdefaultCenter]

    addObserver:self

    selector:@selector(handleAppIsBroughtToForeground:)

    name:UIApplicationWillEnterForegroundNotificationobject:nil];

}


- (void)didReceiveMemoryWarning

{

    [superdidReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

-(void)dealloc{

    AppDelegate *appDelegate = [UIApplicationsharedApplication].delegate;

    [appDelegate removeObserver:selfforKeyPath:@"allNewsItems"];

    [[NSNotificationCenterdefaultCenter]removeObserver:self];

}


- (void) observeValueForKeyPath:(NSString *)keyPath

                       ofObject:(id)object

                         change:(NSDictionary *)change

                        context:(void *)context{

    if ([keyPath isEqualToString:@"allNewsItems"]){

        if ([selfisBeingPresented]){

            [self.tableViewreloadData];

        } else {

            self.mustReloadView = YES;

        }

    }

}

- (void) handleAppIsBroughtToForeground:(NSNotification *)paramNotification{

    if (self.mustReloadView){

        self.mustReloadView =NO;

    [self.tableViewreloadData];

    }

}


#pragma mark - Table view data source


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

{


    return 1;

}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{


    returnself.allNewsItems.count;

}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierforIndexPath:indexPath];

    

    NewsItem *newsItem = self.allNewsItems[indexPath.row];

    cell.textLabel.text = newsItem.text;

    

    return cell;

}



@end


NewsItem.h文件

#import <Foundation/Foundation.h>


@interface NewsItem :NSObject


@property (nonatomic,strong)NSDate *date;

@property (nonatomic,copy)NSString *text;


@end


NewsItem.m文件


#import "NewsItem.h"


@implementation NewsItem


@end


另外记得几点

1,开启这项功能 project serring --> Capabilities tab --> background modes --> 勾选 backgroundfetch 

2,storyboard里面设置好TableViewController的Class,和UITableViewCell的Identifier(@"Cell")

3,运行程序后,home键进入后台

4,Xcode-->Debug-->simulate background fetch,多点几次

5,打印如下:

2014-07-10 17:57:06.057 cookbook7_16[1035:a0b] -[TableViewController viewDidLoad]

2014-07-10 17:57:27.489 cookbook7_16[1035:a0b] -[AppDelegate application:performFetchWithCompletionHandler:]

2014-07-10 17:57:27.490 cookbook7_16[1035:a0b] -[AppDelegate fetchNewsItems:]

2014-07-10 17:57:27.491 cookbook7_16[1035:a0b] self.allNewsItems.cout=2

2014-07-10 17:57:34.842 cookbook7_16[1035:a0b] -[AppDelegate application:performFetchWithCompletionHandler:]

2014-07-10 17:57:34.843 cookbook7_16[1035:a0b] -[AppDelegate fetchNewsItems:]

2014-07-10 17:57:34.844 cookbook7_16[1035:a0b] self.allNewsItems.cout=3


6,模拟器,点击应用进入前台会发现多了记录了



到目前为止,我们可以让后台的程序也能获取数据,但如果程序已经终止了呢?

如果来模拟已经终止的程序,但却能继续获取数据呢?

Xcode -->Manage Schemes --> set --> duplicate -->勾选Launch due to a background fetch event --> OK

如下:



选择刚创建的Scheme在模拟器上运行,这不会启动你的程序,而是在后台发送一个获取数据的信号,这将调用application:performFetchWithCompletionHandler:方法。

2014-07-11 10:33:11.985 cookbook7_16[717:a0b] -[TableViewController viewDidLoad]

2014-07-11 10:33:11.995 cookbook7_16[717:a0b] -[AppDelegate application:performFetchWithCompletionHandler:]

2014-07-11 10:33:11.995 cookbook7_16[717:a0b] -[AppDelegate fetchNewsItems:]

2014-07-11 10:33:11.996 cookbook7_16[717:a0b] self.allNewsItems.cout=2



16.4. Playing Audio in the Background

写一个播放音乐的应用,希望能在后台播放


targets --> Capabilities--> Background Modes -->Audio and Airplay

使用AV Foundation 来播放音频,应用在进入后台后会继续播放

记住要在真机上测试哦,模拟器可不行的


加入AVFounddation.framework包


#import "AppDelegate.h"

#import <AVFoundation/AVFoundation.h>


@interface AppDelegate ()<AVAudioPlayerDelegate>


@property (nonatomic,strong)AVAudioPlayer *audioPlayer;


@end


@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    // Override point for customization after application launch.

    dispatch_queue_t dispatchQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    dispatch_async(dispatchQueue, ^(void) {

        NSError *audioSessionError = nil;

        AVAudioSession *audioSession = [AVAudioSessionsharedInstance];

        [audioSessionsetActive:YESerror:nil];

        if ([audioSession setCategory:AVAudioSessionCategoryPlaybackerror:&audioSessionError]){

            NSLog(@"Successfully set the audio session.");

        } else {

            NSLog(@"Could not set the audio session");

        }

        NSBundle *mainBundle = [NSBundle mainBundle];

        NSString *filePath = [mainBundle pathForResource:@"MySong"

                                                 ofType:@"mp3"];

        NSData   *fileData = [NSData dataWithContentsOfFile:filePath];

        NSError  *error = nil;

        /* Start the audio player */

        self.audioPlayer = [[AVAudioPlayeralloc]initWithData:fileData

                                                        error:&error];

        /* Did we get an instance of AVAudioPlayer? */

        if (self.audioPlayer !=nil){

            /* Set the delegate and start playing */

            self.audioPlayer.delegate =self;

            if ([self.audioPlayerprepareToPlay] && [self.audioPlayerplay]){

                NSLog(@"Successfully started playing...");

            } else {

                NSLog(@"Failed to play.");

            }

        } else {

            /* Failed to instantiate AVAudioPlayer */

        }

    });

    return YES;

}

- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player{

    /* Audio Session is interrupted. The player will be paused here */

}

- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player

                       withOptions:(NSUInteger)flags{

    /* Check the flags, if we can resume the audio, then we should do it here */

    if (flags ==AVAudioSessionInterruptionOptionShouldResume){ [playerplay];

    }

}


- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{

    NSLog(@"Finished playing the song");

    /* The flag parameter tells us if the playback was successfully

     finished or not */

    if ([player isEqual:self.audioPlayer]){

        self.audioPlayer = nil;

    } else {

        /* This isn't our audio player! */

    }

}


启动程序,进入后台,音乐会继续播放,有关播放音频的更多信息,请看12.5节

目前的方式是在进入后台后只能播放完一首歌,如果想继续播放别的歌曲,你可以在结束的时候,重新创建一个新的AVAudioPlayer


16.5. Handling Location Changes in the Background

你写了个应用,需要获取位置信息,你希望在应用进入后台后,还能更新位置信息


当应用在前台运行时,可以通过CLLocationManager的代理消息获得位置更新信息,但当进入后台后,位置代理信息经不能正常发送。他将在你下次进入程序时,一下子把这段时间来的更新信息全部发给你。


如果你想在后台时也能收到位置更新信息,你应该启用位置更新功能。

targets --> Capabilities--> Background Modes -->Loation updates


#import "AppDelegate.h"

#import <CoreLocation/CoreLocation.h>


@interface AppDelegate () <CLLocationManagerDelegate>

@property (nonatomic,strong)CLLocationManager *myLocationManager;

@property (nonatomic,unsafe_unretained,getter=isExecutingInBackground) BOOL executingInBackground;

@end


@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    // Override point for customization after application launch.

    self.myLocationManager = [[CLLocationManageralloc]init];

    self.myLocationManager.desiredAccuracy =kCLLocationAccuracyBest;

    self.myLocationManager.delegate =self;

    [self.myLocationManagerstartUpdatingLocation];

    return YES;

}

- (void)applicationWillResignActive:(UIApplication *)application

{


}


- (void)applicationDidEnterBackground:(UIApplication *)application

{

    self.executingInBackground =YES;

    /* Reduce the accuracy to ease the strain on

     iOS while we are in the background */

    self.myLocationManager.desiredAccuracy =kCLLocationAccuracyHundredMeters;

}


- (void)applicationWillEnterForeground:(UIApplication *)application

{

    self.executingInBackground =NO;

    /* Now that our app is in the foreground again, let's increase the location

     detection accuracy */

    self.myLocationManager.desiredAccuracy =kCLLocationAccuracyBest;

}


- (void)applicationDidBecomeActive:(UIApplication *)application

{

    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

}


- (void)applicationWillTerminate:(UIApplication *)application

{

    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

}


- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation

           fromLocation:(CLLocation *)oldLocation{

    NSLog(@"oldLocation=%@  newLocation=%@",oldLocation,newLocation);

    if ([selfisExecutingInBackground]){

        /* We are in the background. Do not do any heavy processing */

    }else {

        /* We are in the foreground. Do any processing that you wish */

    }

}


@end


模拟器运行打印:

2014-07-11 15:28:22.024 cookbook7_16_5[475:a0b] oldLocation=(null)  newLocation=<+37.78583400,-122.40641700> +/- 5.00m (speed -1.00 mps / course -1.00) @ 7/11/14, 3:28:22 PM China Standard Time

2014-07-11 15:28:23.015 cookbook7_16_5[475:a0b] oldLocation=<+37.78583400,-122.40641700> +/- 5.00m (speed -1.00 mps / course -1.00) @ 7/11/14, 3:28:22 PM China Standard Time  newLocation=<+37.78583400,-122.40641700> +/- 5.00m (speed -1.00 mps / course -1.00) @ 7/11/14, 3:28:23 PM China Standard Time

2014-07-11 15:28:24.016 cookbook7_16_5[475:a0b] oldLocation=<+37.78583400,-122.40641700> +/- 5.00m (speed -1.00 mps / course -1.00) @ 7/11/14, 3:28:23 PM China Standard Time  newLocation=<+37.78583400,-122.40641700> +/- 5.00m (speed -1.00 mps / course -1.00) @ 7/11/14, 3:28:24 PM China Standard Time

2014-07-11 15:28:25.017 cookbook7_16_5[475:a0b] oldLocation=<+37.78583400,-122.40641700> +/- 5.00m (speed -1.00 mps / course -1.00) @ 7/11/14, 3:28:24 PM China Standard Time  newLocation=<+37.78583400,-122.40641700> +/- 5.00m (speed -1.00 mps / course -1.00) @ 7/11/14, 3:28:25 PM China Standard Time

。。。。。。


16.6. Saving and Loading the State of Multitasking Apps

进入后台时保持状态,回到前台时继续进行

下面以游戏的例子说明基本步骤

#import <UIKit/UIKit.h>


@interface AppDelegate :UIResponder <UIApplicationDelegate>


@property (strong,nonatomic)UIWindow *window;


/* Saving the state of our app */

- (void) saveUserScore;

- (void) saveLevelToDisk;

- (void) pauseGameEngine;

/* Loading the state of our app */

- (void) loadUserScore;

- (void) loadLevelFromDisk;

- (void) resumeGameEngine;


@end


#import "AppDelegate.h"


@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    // Override point for customization after application launch.

    return YES;

}

- (void)applicationWillResignActive:(UIApplication *)application

{

    [selfpauseGameEngine];

}


- (void)applicationDidEnterBackground:(UIApplication *)application

{

    [selfsaveUserScore];

    [selfsaveLevelToDisk];

    [selfpauseGameEngine];

}


- (void)applicationWillEnterForeground:(UIApplication *)application

{

    [selfloadUserScore];

    [selfloadLevelFromDisk];

    [selfresumeGameEngine];

}


- (void)applicationDidBecomeActive:(UIApplication *)application

{

    [selfresumeGameEngine];

}


- (void)applicationWillTerminate:(UIApplication *)application

{

    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

}


- (void) saveUserScore{

    /* Save the user score here */

}

- (void) saveLevelToDisk{

    /* Save the current level and the user's location on map to disk */

}

- (void) pauseGameEngine{

    /* Pause the game engine here */

}

- (void) loadUserScore{

    /* Load the user's location back to memory */

}

- (void) loadLevelFromDisk{

    /* Load the user's previous location on the map */

}

- (void) resumeGameEngine{

    /* Resume the game engine here */

}


@end



16.7. Handling Network Connections in the Background

通过NSURLConnection发送接收数据,如何利用多任务环境,使其不至于连接失败呢

通过

+ (void)sendAsynchronousRequest:(NSURLRequest*) request

                          queue:(NSOperationQueue*) queue

              completionHandler:(void (^)(NSURLResponse* response,NSData* data,NSError* connectionError)) handlerNS_AVAILABLE(10_7,5_0);


这个方法,当它在下载数据时,应用进入后台,iOS会自动把下载过程置为挂起状态,当重新进入前台,iOS下载过程将继续进行,而你呢一行代码都不用多写。



16.8. Opting Out of Multitasking

不想要多任务

.plist 文件中增加UIApplicationExitsOnSuspend键设置值为true

在早期不支持多任务的iOS版本中,这个值将被忽略。
























0 0
原创粉丝点击