在ios7之前,当你的app进入后台之后,那么你能做的事情非常少。只有VOIP和定位服务等一些基本服务可以在后台运行,对于其他的的服务,只能通过后台任务来执行,而且仅限制在几分钟之内。如果你想下载一个很大的视频或者备份你的照片到服务器,那么你可能完不成这些任务,进程就被挂起了。
iOS7添加了两个新的接口,通过这两个接口你能够在后台更新的你UI和内容。第一,background fetch,ios7允许定期从网络获取新的内容。第二,远程通知,这是一个新的特性,利用了推送来通知app某些事件发生了。这两个新的特性能够让你在后台进行网络传输。
以上的两个特性都是通过app代理实现的,在程序被挂起之前你有30秒的时间来执行程序。
对于多任务来所,唯一明显的变化是新的app多任务的切换,当程序进入后台的时候会展示一个应用程序的快照。但是这个快照是可以更新的,在下面我们将看到如何更新这个快照。
Background Fetch
Background Fetch是一种智能的轮训机制,对于更新比较频烦的应用来说能表现出非常好的效果。系统会根据用户的行为来唤醒程序,并且在程序启动之前触发后台获取。例如,用户每天在1点中使用我们的app,系统会学习并且适应这个用户的习惯,所以,系统会每天在1点之前的一段时间内进行后台获取,为了减少电池的损耗,ios统一通过设备的无线电来进行获取。
第一步,如果你想进行后台获取,那么你需要在plist文件中指定UIBackgroundModes键,指定这个键的最简单的办法是利用xcode5的"Capabilities"选项卡,直接打开Background Modes进行勾选,或者你也可以手动编辑:
<key>UIBackgroundModes</key> <array> <string>fetch</string> </array>接下来告诉系统你想多久fetch一次:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum]; return YES; }默认的fetch间隔是永远不进行fetch,所以你需要告诉ios一个时间间隔,否则程序在后台永远不会被唤醒。UIApplicationBackgroundFetchIntervalMinimum告诉系统去管理这个程序的唤醒,越often越好。但是如果你觉得这样做没必要的话那么你需要自己指定一个时间间隔。比如一个天气的app每小时进行一次更新,那么系统至少要等一个小时进行fetch。
如果你的app允许用户logout,并且退出后又没什么新的数据,那么设置这个间隔为UIApplicationBackgroundFetchIntervalNever,这样可以节约资源。
最后一步是在你的delegate实现如下代码:
- (void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; NSURL *url = [[NSURL alloc] initWithString:@"http://yourserver.com/data.json"]; NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { completionHandler(UIBackgroundFetchResultFailed); return; } // Parse response/data and determine whether new content was available BOOL hasNewData = ... if (hasNewData) { completionHandler(UIBackgroundFetchResultNewData); } else { completionHandler(UIBackgroundFetchResultNoData); } }]; // Start the task [task resume]; }这个代理就是app被唤醒工作的地方,记住你只有30秒时间进行update,当update结束之后你需要调用处理结束程序。
完成处理程序有两个目的,一是衡量启动这个进程耗费的资源,并且根据传递的UIBackgroundFetchResult这个参数来判断新的数据是否available。二,当你调用完成处理程序的时候,程序的快照被更新了,在多任务的新APIs当中,所有完成处理程序的快照行为是相同的。
在程序的世界当中,你应改传递完成处理程序最为你的程序的子组建。当数据处理结束的时候调用这个完成处理程序,更新你的UI。
在这一点上,你可能好奇ios是如何在后台更新你的UI的,生命周期在后台获取时是什么样的。如果你的程序当前被挂起了,那么系统会唤醒你的程序在调用application:performFetchWithCompletionHandler:之前。如果你当前的程序没有运行,那么系统将会launch它,调用一些usual方法,像application: didFinishLaunchingWithOptions:,你完全可以把这个过程想象为用户从Springboard启动了程序,只不过你看不到UI和渲染的屏幕。
在大多数情况下,在程序启动的时候我们要做的事情都是相同的,不管是前台启动还是后台启动。但是我们仍然可以捕捉到是从什么状态下启动程序的,通过查看UIApplication的applicationState属性:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSLog(@"Launched in background %d", UIApplicationStateBackground == application.applicationState); return YES; }
测试Background Fetch
有两种方法来测试Background fetch,最简单的方法是从xcode启动的你的app,然后点击debug菜单下的Simulate Background Fetch,另外一种方式是制定一个计划,让xcode按照计划来运行程序,在Product菜单下选择Scheme,然后 Manage Schemes,在这你可以编辑一个计或者添加一个计划。
Remote Notifications
推送的意义和功能这里不再赘述。当你接到推送的时候会调用
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSLog(@"Remote Notification userInfo is %@", userInfo); NSNumber *contentID = userInfo[@"content-id"]; // Do something with the content ID completionHandler(UIBackgroundFetchResultNewData); }
同样,系统给你30秒时间来处理这些事务。
NSURLSession and Background Transfer Service
NSURLSession在ios7 当中是一个新的类,它是在基础网络中的新技术。为了替代NSURLConnection,一些相似的概念和类被封装了,像 NSURL
, NSURLRequest
,和NSURLResponse。所以你将用
NSURLSessionTask替代NSURLConnection来进行网络请求和处理响应。有三种类型的session tasks,data,download,upload, 这三种用法差不多,会了一个其他的就会了。
一个NSURLSession标示了一个或多个NSURLSessionTask,它会根据创建这些task的NSURLSessionConfiguration的不同而作出不同的行为。你可能会创建NSURLSession
s,根据NSURLSessionConfiguration进行分组。为了能够跟后台进行互动,你需要创建[NSURLSessionConfiguration backgroundSessionConfiguration],使用了这个sesion的task在外部的一个进程运行,即使你的程序崩溃了或者挂起了或者被杀死了,那么这个进程不受影响。
NSURLSessionConfiguration允许你设定HTTP头,允许你指定缓存策略,限制使用蜂窝网络等。一个需要注意的地方是discretionary标志,它标识系统是否以最佳性能调度任务,也就是说,你的设备需要在wifi下并且电源充足,否则后台传输不能够被执行。
NSURLSessionDownloadTask
- (NSURLSession *)backgroundURLSession { static NSURLSession *session = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *identifier = @"io.objc.backgroundTransferExample"; NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier]; session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; }); return session; }
//接到推送的委托
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSLog(@"Received remote notification with userInfo %@", userInfo); NSNumber *contentID = userInfo[@"content-id"]; NSString *downloadURLString = [NSString stringWithFormat:@"http://yourserver.com/downloads/%d.mp3", [contentID intValue]]; NSURL* downloadURL = [NSURL URLWithString:downloadURLString]; NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL]; NSURLSessionDownloadTask *task = [[self backgroundURLSession] downloadTaskWithRequest:request]; task.taskDescription = [NSString stringWithFormat:@"Podcast Episode %d", [contentID intValue]]; [task resume]; completionHandler(UIBackgroundFetchResultNewData); }
我们创建了一个下载任务,并对它的请求进行了配置,还提供了接下来要用到的描述等。你必须要要执行[task resume]来启动任务,这样任务才能在app挂起状态下启动执行。
现在我们要做的是实现NSURLSessionDownloadDelegate这个委托,用来接收任务完成的消息。在session的声明周期里如果你想要处理认证或者其他的事件你需要实现
NSURLSessionDelegate
委托或者 NSURLSessionTaskDelegate委托。你应该看看苹果的文档
https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/URLLoadingSystem/NSURLSessionConcepts/NSURLSessionConcepts.html#//apple_ref/doc/uid/10000165i-CH2-SW42,它介绍了各种类型的session tasks的整个声明周期。
NSURLSessionDownloadDelegate
的所有方法都是需要实现的,虽然我们的例子只用了[NSURLSession downloadTask:didFinishDownloadingToURL:] 这个。当任务结束下载的时候,问及那被存储到了一个临时目录,你必须移动或copy这个文件到你的app,否则下次回调这个代理的时候这个文件会被删除。下面是这些代理:
#Pragma Mark - NSURLSessionDownloadDelegate - (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSLog(@"downloadTask:%@ didFinishDownloadingToURL:%@", downloadTask.taskDescription, location); // Copy file to your app's storage with NSFileManager // ... // Notify your UI }
- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { }
- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { }
如果当session task运行完成了,而此时的app又是在前台活跃的,那么上面的代码就足够了。实际上,在大对数情况下你的app没在运行状态或者被挂起了,在这种情况下你必须实现两个application 委托方法,这样系统就能唤醒的你程序。不想之前的委托,application委托方法被回调两次,这时你的session或者task的委托可能会受到几条消息。application的application: handleEventsForBackgroundURLSession:函数在 NSURLSession
delegate回调之前被调用,URLSessionDidFinishEventsForBackgroundURLSession
在之后被调用,在前面的委托里你需要存储完成处理代码,在接下来的委托里通过完成处理程序更新你的UI。
- (void) application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler { // You must re-establish a reference to the background session, // or NSURLSessionDownloadDelegate and NSURLSessionDelegate methods will not be called // as no delegate is attached to the session. See backgroundURLSession above. NSURLSession *backgroundSession = [self backgroundURLSession]; NSLog(@"Rejoining session with identifier %@ %@", identifier, backgroundSession); // Store the completion handler to update your UI after processing session events [self addCompletionHandler:completionHandler forSession:identifier]; } - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { NSLog(@"Background URL session %@ finished events.\n", session); if (session.configuration.identifier) { // Call the handler we stored in -application:handleEventsForBackgroundURLSession: [self callCompletionHandlerForSession:session.configuration.identifier]; } } - (void)addCompletionHandler:(CompletionHandlerType)handler forSession:(NSString *)identifier { if ([self.completionHandlerDictionary objectForKey:identifier]) { NSLog(@"Error: Got multiple handlers for a single session identifier. This should not happen.\n"); } [self.completionHandlerDictionary setObject:handler forKey:identifier]; } - (void)callCompletionHandlerForSession: (NSString *)identifier { CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey: identifier]; if (handler) { [self.completionHandlerDictionary removeObjectForKey: identifier]; NSLog(@"Calling completion handler for session %@", identifier); handler(); } }
如果你的程序没有在前台运行,那么这两阶段的处理,在background transfer结束的时候,对于更新你的UI来说是必要的。如果在background transfer结束的时候,你的程序根本没启动,那么系统会启动你的程序,以前的application和session的delegate会在application:didFinishLaunchingWithOptions:之后执行。
Configuration and Limitation
我们已经简要的介绍后台传输服务的耗电情况,但是你应该通读上面提供的文档,看看NSURLSessionConfiguration
的options当中哪条最适合你。除了download tasks ,NSURLSession
也全面支持upload tasks。
background sessions也有一些限制,在NSURLSession你不能使用block-based的回调,在后台启动程序是一件很费资源的事,所以http的重定向经常被用到。background sessions只支持http和https。系统会根据可用的资源对background sessions进行优化。所以你的background session不可能总在后台服务。
NSURLSessionDataTasks
只能短期的存活,进行小的请求,不能用来下载和上传。
相关推荐
7. **管理任务**: 为了控制多任务下载,可以使用数组来存储所有任务,根据需要暂停、恢复或取消特定任务。当达到某个限制(如网络连接状况、内存使用情况等)时,可以暂停部分任务,待条件改善后再继续。 8. **更新...
在iOS系统中,多任务切换界面的设计一直备受用户喜爱,特别是iOS9引入的卡片流式切换方式,它为用户提供了直观、流畅的多任务管理体验。这个名为"仿iOS9多任务切换的卡片流"的开源项目,目标是复现这一功能,并允许...
在iOS 4中,苹果引入了多任务处理功能,这是对操作系统的一大革新,极大地提升了用户体验。在此之前,iOS设备只能在一个应用程序之间切换,而不能同时运行多个应用。这一更新使得iOS平台更加接近桌面操作系统,为...
清除iOS 7 多任务调整。 通过长按应用程序快照和应用程序图标(如 1.2betas)杀死应用程序切换器中的所有应用程序。 新增功能—— v1.2 如果正在播放音乐,则在杀死所有应用程序时排除正在播放的应用程序。 长按应用...
在iOS系统中,多任务手势是一项非常实用的功能,它允许用户通过简单的手势操作来实现应用程序之间的切换、关闭等操作,极大地提升了使用效率。在iOS 4.3.2版本中,这一特性得到了优化和更新,使得用户体验更加流畅。...
6. **多线程编程**:如果源码中包含了后台处理或异步任务,那么会看到GCD(Grand Central Dispatch)或者NSOperationQueue的使用,这些都是iOS中处理并发的关键技术。 7. **iOS7特有功能**:由于源码基于iOS7,可能...
3. **多任务处理**:iOS 7支持后台模式,如后台应用程序刷新、后台音频播放和定位服务等。开发者将了解如何利用这些功能,使应用即使在后台也能保持活跃,提供持续的服务。 4. **UI Dynamics**:iOS 7引入了物理...
- **多任务处理**:iOS 7优化了多任务处理机制,提供了更智能的应用程序切换器,能够显示应用的预览画面,便于用户快速切换。 - **通知中心**:扩展的通知中心分为“今天”、“所有通知”和“错过”三个部分,让用户...
7. **多任务管理**:iOS7的多任务管理界面采用卡片式展示,开发者需要理解新的多任务API来优化应用程序的后台行为。 8. **手势识别**:iOS7增强了手势识别,如滑动返回、轻扫唤醒等。开发者需熟悉Gesture ...
在iOS 7中,苹果引入了一系列重大更新,包括全新的扁平化设计、控制中心、多任务处理、以及对通知中心的改进。这些变化要求开发者重新考虑他们的设计策略和编程实践。本教程将引导读者了解并掌握这些关键知识点: 1...
iOS 7引入了许多新特性,如扁平化设计、控制中心、多任务处理等,这些在书中都会有专门章节进行讨论。开发者如何利用这些新功能来提升用户体验也会被详细讲解。 为了帮助读者更好地理解并实践所学知识,本书附带的...
除此之外,iOS的多任务处理也是需要关注的点。在Shutterbug中,你可能需要处理后台图片的上传或处理,这就涉及到后台模式的配置和Grand Central Dispatch (GCD)的使用,以确保应用在不同场景下都能流畅运行。 最后...
在本书中,Vandad Nahavandipoor利用自己在iOS开发领域丰富的经验,为读者提供了一系列的编程食谱,每个食谱都是一步一步地指导开发者完成特定任务。这种按需解决问题的方式,对于快速学习iOS 7的开发和高级技巧非常...
iOS 7引入了若干多任务处理技术,其中之一就是Background Fetch,这是一种高效的方式,让应用能在系统认为合适的时机唤醒并执行短暂的任务,如更新内容或下载数据。本篇文章将深入探讨Background Fetch及其在实际...
在Android平台上实现一个类似iOS 7的解锁界面是一项有趣且具有挑战性的任务,因为它涉及到对用户界面设计的深入理解和对Android开发技术的熟练掌握。在这个项目中,我们将探讨如何使用Android SDK来创建一个模仿iOS ...
- 安装【CardSwitcher】以获得iOS7风格的多任务切换界面,按个人需求开启相关功能。 7. **快捷开关**: - 安装【Ncsettings】,它包含iOS7风格的快捷开关,并且原生支持中文。 - 将【Ncsettings】文件夹内容放入...
在iOS平台上,多任务执行是一..."iPhone多任务执行源码 Demo"提供了一个实践平台,帮助开发者深入学习iOS多任务的实现细节,通过阅读和分析源码,可以更好地掌握如何在实际项目中实现多任务功能,提升应用的用户体验。
7. **Core Data**:iOS7对Core Data进行了优化,包括更好的多线程支持和性能提升。开发者应了解如何高效地使用Core Data进行数据持久化。 8. **Background App Refresh**:此功能允许应用程序在后台执行任务,如...
在iOS 7中,苹果引入了一系列重要的设计和功能更新,如扁平化设计、控制中心、多任务处理等。开发者需要了解这些变化,以便他们的应用能够充分利用新系统提供的特性并提供优秀的用户体验。 课程涵盖了以下关键知识...