多任务(2)
来源:互联网 发布:砂浆稠度试验数据 编辑:程序博客网 时间:2024/06/15 15:54
3.在后台接收本地通知
3.1. 问题
你希望即使在程序没有运行时,可以向用户展示一个警告。
你希望在没有使用推送消息的情况下,可在程序中创建本地警告。
3.2. 方案
实例化一个 UILocalNotification 对象,然后使用 UIApplication 的scheduleLocalNotification:实例方法安排它:
//badge /bædʒ/ n. 徽章;标记
- (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 = [[UILocalNotificationalloc]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
确保我们有能让用户点击进入App的按钮*/
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 = [NSTimeZonesystemTimeZone];
notification.timeZone = timeZone;
/* Schedule the delivery of this notification x seconds from now */
NSDate *today = [NSDatedate];
NSDate *fireDate = [today dateByAddingTimeInterval:paramSecondsFromNow];
NSCalendar *calendar = [NSCalendarautoupdatingCurrentCalendar];
NSUInteger dateComponents =NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond;
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;
[[UIApplicationsharedApplication]cancelAllLocalNotifications];
[[UIApplicationsharedApplication]scheduleLocalNotification:notification];
return YES;
}
本地通知是一个警告视图(UIAlertView 类型的对象),在你的程序运行于后台时或者 根本没运行的情况下展现给用户。你可以使用 UIApplication 的scheduleLocalNotification: 实例方法安排本地通知的发送,cancelAllLocalNotifications 实例方法取消所有进行中的本地通知的发送。
你也可以要求 iOS 将来即使在程序没有运行时向用户发送本地通知。这些通知可以是重复的--比如,每周的某个时间。但是,在你指定通知的触发日期时必须特别小心。
举例来说,假设现在是伦敦时间 13:00,现在的时区是 GMT+0,你的程序正运行在用户 的设备上。你想在 14:00 向用户发送通知,即使那时程序已退出。现在你的用户在伦敦盖特 维克机场飞往斯德哥尔摩的飞机上,斯德哥尔摩的时区是 GMT+1.如果飞行需要 30 分钟, 用户到达斯德哥尔摩的时间将是 GMT+0 时区 13:00。但是,当它到达时,iOS 设备会侦测到 时区的变化,然后将用户设备的时间调到了 14:30。你的通知本应该在 14:00(GMT+0) 时发生,所以当时区变化时,iOS 设备检测到通知应该被显示了(晚了 30 分钟,实际上是新时区),然后显示该通知。
问题是你的通知本来应该在 14:00 GTM+0 或者 15:00 GTM+1 显示,而不是 14:30 GTM+1。为了处理这类情况(由于现代的旅行习惯,它发生几率比你认为的更频繁),当 你为通知指定了触发日期和时间,你还应该为这个时间指定时区。
前面的代码并不包括警告视图,你需要编写它来向用户显示些东西。我们继续向程序添加代码,看看在不同情景下面 iPhone 模拟器会发生什么。现在我们要到 application:didReceiveLocalNotification:方法中,看看是否有本地通知唤醒了我们的应用。如果没有的话,我们安排一个:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
id scheduledLocalNotification = [launchOptionsvalueForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (scheduledLocalNotification != nil) {
/* We received a local notification while our application wasn't running. You can now typecase the ScheduledLocalNotification variable to UILocalNotification and use it in your application
*/
NSString *message =@"Local Notification Woke Us Up";
[[[UIAlertViewalloc]initWithTitle:@"Notification"message:message delegate:nilcancelButtonTitle:@"OK"otherButtonTitles:nil,nil]show];
}else{
NSString *message =@"A new instant message is available. Would you like to read this message?";
/* If a local notification didn't start our application,
then we start a new local notification */
[selflocalNotificationWithMessage:message actionButtontitle:@"Yes"launchImage:nilapplicationBadge:1secondsFromNow:10.0fuserInfo:nil];
message = @"A new Local Notification is set up to be displayed 10 seconds from now";
[[[UIAlertViewalloc]initWithTitle:@"Set Up"message:message delegate:nilcancelButtonTitle:@"OK"otherButtonTitles:nil,nil]show];
}
return YES;
}
/接受本地推送
//最后强调一点,我们会在程序中处理 UIApplicationDelegate的 application:didReceiveLocalNotification:方法,来确保用户打开应用时,因为收到本地通知而向她显示了一条消息:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
NSString *message =@"The Local Notification is delivered.";
[[[UIAlertViewalloc] initWithTitle:@"Local Notification"message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil,nil] show];
}
// 创建一个本地推送 UILocalNotification *notification = [[[UILocalNotification alloc] init] autorelease]; //设置10秒之后 NSDate *pushDate = [NSDate dateWithTimeIntervalSinceNow:10]; if (notification != nil) { // 设置推送时间 notification.fireDate = pushDate; // 设置时区 notification.timeZone = [NSTimeZone defaultTimeZone]; // 设置重复间隔 notification.repeatInterval = kCFCalendarUnitDay; // 推送声音 notification.soundName = UILocalNotificationDefaultSoundName; // 推送内容 notification.alertBody = @"推送内容"; //显示在icon上的红色圈中的数子 notification.applicationIconBadgeNumber = 1; //设置userinfo 方便在之后需要撤销的时候使用 NSDictionary *info = [NSDictionary dictionaryWithObject:@"name"forKey:@"key"]; notification.userInfo = info; //添加推送到UIApplication UIApplication *app = [UIApplication sharedApplication]; [app scheduleLocalNotification:notification]; }第二步:接收本地推送
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification*)notification{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"iWeibo" message:notification.alertBody delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alert show]; // 图标上的数字减1 application.applicationIconBadgeNumber -= 1; }第三步:解除本地推送
// 获得 UIApplication UIApplication *app = [UIApplication sharedApplication]; //获取本地推送数组 NSArray *localArray = [app scheduledLocalNotifications]; //声明本地通知对象 UILocalNotification *localNotification; if (localArray) { for (UILocalNotification *noti in localArray) { NSDictionary *dict = noti.userInfo; if (dict) { NSString *inKey = [dict objectForKey:@"key"]; if ([inKey isEqualToString:@"对应的key值"]) { if (localNotification){ [localNotification release]; localNotification = nil; } localNotification = [noti retain]; break; } } } //判断是否找到已经存在的相同key的推送 if (!localNotification) { //不存在初始化 localNotification = [[UILocalNotification alloc] init]; } if (localNotification) { //不推送 取消推送 [app cancelLocalNotification:localNotification]; [localNotification release]; return; } }
4.在后台播放声音
4.1. 问题
你在写一个播放声音文件的程序(比如一个音乐播放器),你希望即使程序在后台运行
4.2. 方案
在你程序的主.plist(main .plist)创建一个新的 array 键。设置键的名字为 UIBackgroundModes(instead of Required background modes)将 audio (App plays audio or streams audio/video using AirPlay)赋值给这个新的键。下面是一个.plist 文件内容的例子,加入了前面提到的键和值:时,也能播放声音文件。
现在你可以使用 AV Fondation 来播放声音文件了,即使程序在后台,你的声音文件还是会被播放。
4.3. 讨论
在 iOS 中,即使程序在后台运行,他还是可以请求继续播放声音文件。AV Foundation 的 AVAudioPlayer 是个易于使用的播放器,我们将在本节中使用它。我们的任务是启动一个音频播放器并播放一首简单的歌曲,当歌曲在播放时,按下 Home 按钮将程序转入后台。如果我们在程序的.plist 文件中包含了 UIBackgroundModes 键,iOS 将继续播放音频播放器正在播放的音乐,即使程序在后台。当程序在后台时,程序应该只播放音乐以及音乐播放器运行时必要的数据。我们不应该运行其他任何的任务,比如显示新的屏幕等等。
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@interface ViewController :UIViewController<AVAudioPlayerDelegate>
@property (nonatomic,strong) AVAudioPlayer *audioPlayer;
@end
当我们的程序被打开时,我们会分配和初始化我们的音频播放器,读取一个名为 MySong.mp4 的文件的内容到 NSData 的实例中,并在我么的音频播放器的初始化过程中使 用这个数据:
- (void)viewDidLoad {
[superviewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
NSError *audioSessionError = nil;
AVAudioSession *audioSession = [AVAudioSessionsharedInstance];
if ([audioSession setCategory:AVAudioSessionCategoryPlaybackerror:&audioSessionError]) {
NSLog(@"Successfully set the audio session.");
}else{
NSLog(@"Could not set the audio session");
}
NSString *filePath = [[NSBundlemainBundle]pathForResource:@"MySong"ofType:@"mp3"];
NSData *fileData = [NSDatadataWithContentsOfFile:filePath];
NSError *error = nil;
self.audioPlayer = [[AVAudioPlayeralloc]initWithData:fileDataerror:&error];
if (self.audioPlayer !=nil) {
self.audioPlayer.delegate =self;
if ([self.audioPlayerprepareToPlay] && [self.audioPlayerplay]) {
NSLog(@"Successfully started playing...");
}else{
NSLog(@"Failed to play.");
}
}
});
}
5.在后台处理位置变化
5.1. 问题
你在写一个程序,它的主要功能是使用 Core Location 来处理位置变化。你希望即使程序在后台时,也能获得 iOS 设备位置变化的通知。
5.2. 方案
在你的主程序.plist 文件(main application .plist file)中,向 UIBackgroundModes 键中添加 location (App registers for location updates)值,如下:
5.3. 讨论
当你的程序在前台运行时,你可以从一个 CLLocationManager 实例获得委托消息,在 iOS 检测到设备移动到新位置时告诉你这一变化。但是,如果你的程序被送到后台并且不再 处于活动状态,这些位置委托消息一般是不会被发给你的程序的。实际上,他们会在你的程 序回到前台后,批量的被发送。
如果在后台时,你仍然想能够获得用户设备位置改变的消息,你就必须在你的主程序.plist文件(main application .plist file)中,向UIBackgroundModes键中添加location值, 就像本小节的“方案”中指出的那样。这样一旦程序转到后台,你的程序将不断的获得设备位置变化的消息。让我们在一个仅有应用程序委托的简单程序中测试一下。
在这个应用中,我打算在 app 委托中保存一个布尔值,我将它取名为 executingInBackground。当程序退到后台,我将他设置为 YES,回到前台时,我会将它设置 为 NO。当我们得到 Core Location 的位置更新消息,我会检查这个标志,如果此标志为 YES,我们就不做任何密集计算或者 UI 更新,因为,我们的程序在后台,作为一个负责的程序员,我们不应该在程序运行在后台时,做开销很大的处理。但是如果我们的应用在前台,我们就拥有设备所有的处理能力来做我们想做的一般处理。程序在前台或是后台时,我们也会尝试得到最佳的位置变化精度,我们将确定在位置更新时请求更低的精度,来减轻位置感应器的压力。让我们继续定义我们的 app 委托吧:
#import <CoreLocation/CoreLocation.h>
CLLocationManagerDelegate
@property (nonatomic,strong) CLLocationManager *myLocationManager;
@property (nonatomic,unsafe_unretained,getter=isExecutingInBackground)BOOL executingInBackground;
.m
- (void)createLocationManager{
//创建和启动位置管理器
self.myLocationManager = [[CLLocationManageralloc]init];
//desired 渴望的 accuracyn /'ækjʊrəsɪ/ 精确(性),准确(性)
self.myLocationManager.desiredAccuracy =kCLLocationAccuracyBest;
self.myLocationManager.delegate =self;
[self.myLocationManagerstartUpdatingLocation];
}
你可以看到,我们将位置管理器的要求精度设为了最高级别。但是,当我们退到后台 时,希望降低这个精度来让 iOS 稍稍休息一下:- (void)applicationDidEnterBackground:(UIApplication *)application{
self.executingInBackground =YES;
self.myLocationManager.desiredAccuracy =kCLLocationAccuracyHundredMeters;
}
当我们的应用从后台激活时,我们可以将精度改到最高级别:
- (void)applicationWillEnterForeground:(UIApplication *)application{
self.executingInBackground =NO;
self.myLocationManager.desiredAccuracy =kCLLocationAccuracyBest;
}
而且,当程序运行在后台时,我们希望避免在从位置管理器得到新位置时进行密集处理,所以我们需要对位置管理器的 locationManager:didUpdateToLocation:fromLocation: 委托方法做如下处理:- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
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 */
}
}
简单的法则是,当我们在后台时,我们应该使用尽可能少量的内存和处理器能力来满足 程序的需要。所以,通过在后台时降低位置管理器的精度,我们就减少了对 iOS 必须向我们 程序发送新位置消息的处理。6.保存和加载多任务的iOS程序的状态
6.1. 问题
你希望在程序被送到后台时保存你 iOS 应用的状态,并在程序回到前台时恢复到和之前相同的状态。
6.2. 方案
使用发送到你的应用程序委托的 UIApplicationDelegate 协议消息,和 iOS 发送消息的组合,来保存你的多任务程序的状态。
6.3. 讨论
a.
当一个空的 iOS 程序(仅有一个窗口没有其他任何代码的程序)在支持多任务的 iOS 设备上第一次运行时,下列的 UIApplicationDelegate 消息将被发送给你的应用程序委托,以下面的顺序:
1. application:didFinishLaunchingWithOptions:
2. applicationDidBecomeActive:
如果用户按下了 iOS 设备上的 Home 按钮,你的应用程序委托将收到按顺序下列消息:
1. applicationWillResignActive:
2. applicationDidEnterBackground:
一旦程序在后台,用户可以按下 Home 按钮两次,从后台程序列表中选择我们的程序。 程序一旦再次回到前台,我们将以下面的顺序在应用程序委托中收到这些消息:
1. applicationWillEnterForeground:
2. applicationDidBecomeActive:
当我们的程序被送到后台或回到前台时,除了这些消息,我们还会从 iOS 收到各种通知消息。
为了保存和加载你的应用的状态,你需要细心考虑需要在转入后台时暂停并在回到前台时恢复的任务。让我给你个例子。如下节所提到的,网络间接可以被系统本身轻易的恢复,所以我们大概不必再从网络下载文件的情况下做些特别的事情。但是,举个例子,如果你在写一个游戏,你最好在你的程序被送到后台时监听 iOS 发送的通知,然后相应的行动。在那种情况下,你可以简单的将游戏引擎置为暂停状态。如果有必要,你也可以将声音引擎暂停。
b.
在程序被送往后台之后,你有 10 秒钟来保存任何为保存的数据,以及为任何时候用户 将程序带回前台做好准备。如果需要的话,你可以选择要求更多的执行时间(关于这点更多的信息参见 2 小节)。
让我们用一个例子展示如何保存程序的状态。假设我们在为 iOS 编写一个游戏。当我们的游戏被送到后台,我们想:
1.将游戏引擎置为暂停状态。
2.将用户的分数保存到磁盘。
3.保存当前关卡的数据到磁盘。这包括用户在关卡中的位置,关卡的物理参数,照相机位置,等等。
当用户重新打开程序,将程序带回前台,我们想:
1.从磁盘加载用户的分数。
2.从磁盘加载上次用户在玩的关卡。
3.恢复游戏引擎
c.
现在假设我们的应用程序委托就是游戏引擎。然我们在它的头文件中定义一些方法:
.h
#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
.m
在我们的应用程序委托中我们将对这些方法进行站位实现(place stub implementations)。
- (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 */
}
- (void)applicationWillResignActive:(UIApplication *)application {
[selfpauseGameEngine];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[selfresumeGameEngine];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
[selfsaveUserScore];
[selfsaveLevelToDisk];
[selfpauseGameEngine];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[selfloadUserScore];
[selfloadLevelFromDisk];
[selfresumeGameEngine];
}
不是所有程序都是游戏。但是,在 iOS 的多任务环境中,你可以使用这项技术来保存和加载程序的状态。
- 多任务(2)
- 多任务--任务管理
- 多任务和多线程(2)
- 多任务
- 多任务
- 多任务
- 多任务
- 多任务
- 多任务
- 任务2
- 任务 2
- 任务2
- 多任务 任务的划分
- Windows多线程多任务设计初步2
- 第四周任务2:多文件组织项目
- 第四周任务2:多文件组织项目
- A.2多任务网段扫描练习
- 第二十章 多任务和多线程(多任务的各种模式2)
- Tomcat ClassLoader
- 使用钩子函数的注意事项
- 开放平台安全性考虑
- 日志文件的处理和分析
- java学习--IO流(2)
- 多任务(2)
- hibernate中的关联映射图解 ---- 一对多
- 线程同步方式
- Tomcat优化配置
- SNS开放平台产业链
- androd剪切板功能
- tomcat启动报错 IOException while loading persisted sessions: java.io.EOFException
- Citrix破解合集
- Android简易电话拨号器