转载自:http://www.devdiv.com/thread-47004-1-1.html
今天星期五,过来发个帖子,预祝大家周末愉快!
IOS的多任务机制并非严格意义上的多任务,要想了解其原理的,我推荐看一下Drops的文章,里面两幅原理图画的很详细,一目了然。下面是其中的一副:
通过查看官方文档,我们了解到,其后台运行机制,允许三种服务在后台长时间运行,分别是
1. 位置服务
2. 音乐播放
3. VoIP
苹果对于这三种服务有一句话是这样说的,“Such applications do not run continuously but are woken up by the system frameworks at appropriate times to perform work related to those services.”
所以他们也只是适时的被系统唤醒。其什么时候停止呢?拿音乐播放来说,“if the application stops playing audio, the system suspends it.”系统将会在停止播放音频的时候立即将其挂起。这些都不是我今天要说的重点。
我这里说的是另外一种后台运行机制,需要在有限的时间内完成的后台任务(官方原文:Finite Length Task)。
为什么说这个?因为前些天在坛子里看到有人问起关于多任务的问题,他举的例子是一个第三方闹钟,进入后台后,能不能完成5分钟后完成响铃提醒的任务。(因为不记得是哪个帖子,所以这里不给链接了,见谅!)
我看到了很多给他的回答,都说明了一个问题,那就是应用程序进入后台后,是被系统挂起的,不能继续执行代码。
也许是我个人理解有问题,这样描述,严格的来说是不正确的。因为苹果给了我们一定的时间让我们在后台做我们没做完的事情。
这里就拿发帖人举的例子,进入后台5分钟后响铃提醒。单从这个描述来说,实现这个功能是没有问题的(记住仅仅只是5分钟哦,看完后面的你就知道是为什么了)。
其实现方式,大致上分为以下步骤:
1. 向系统发出执行后台任务的申请,并定义当任务执行时间超过系统限定的时间后,进行哪些操作来保存数据和状态。引用一段官方代码,如下:
- UIApplication* app = [UIApplication sharedApplication];
- bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
- [app endBackgroundTask:bgTask];
- bgTask = UIBackgroundTaskInvalid;
- }];
复制代码
2. 把自己要执行的任务以block的形式添加到系统的异步任务队列中去(官方推荐的是异步,我用同步实现了那个闹钟功能,详见附件中的代码包)。官方代码如下:
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- // 此处添加你要完成的任务
- // 任务做完以后通过下面的代码通知系统该任务结束了,如果没有其他的任务,那么可以将进程挂起了。
- [app endBackgroundTask:bgTask];
- bgTask = UIBackgroundTaskInvalid;
- });
复制代码
/**********************Split line for coder*********************/
通过上面的描述,我相信很多人已经知道这个闹钟该怎么去实现了。不过发扬一下共享精神,我还是把我的简单实现在这里说一下。
0. 说明一下dispatch_block_t这个类型,这个类型就是block类型,从字面上来讲,就是一个程序块。将你要执行的代码放到"^{"和"}"之间,就变成了一个block对象(姑且就叫它对象吧)。例如:
- dispatch_block_t block = ^{
- NSLog(@"Hello DevDivers!");
- };
复制代码
1. 定义一个定时器用于程序激活状态的时候进行计时。
2. 定义两个宏用来写那些很麻烦的block结构,注意,我这里用的是同步队列。
- #define DoorsBgTaskBegin() { \
- UIApplication *app = [UIApplication sharedApplication]; \
- UIBackgroundTaskIdentifier task = [app beginBackgroundTaskWithExpirationHandler:^{ \
- [app endBackgroundTask:task]; \
- /* task = UIBackgroundTaskInvalid; */ \
- }]; \
- dispatch_block_t block = ^{ \
- #define DoorsBgTaskEnd() \
- [app endBackgroundTask:task]; \
- /* task = UIBackgroundTaskInvalid; */ \
- }; \
- dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); \
- }
复制代码
3. 在程序启动的时候把定时器启动,这一篇重点不在定时器,所以就不对定时器做过多描述了。
4. 在applicationDidEnterBackground中来做我们的闹钟。
4.1 首先把三个变量说明一下
- static int giAlarmInterval = 5 * 60; // 5分钟,闹钟时间间隔
- const int kTimerInterval = 1 * 60; // 1分钟,定时器时间间隔
- const int kSysMaxTimePerBgTask = 10 * 600; // 10分钟,目前版本系统为每个后台任务分配的最大时间
复制代码
4.2 执行代码
- - (void)applicationDidEnterBackground:(UIApplication *)application {
- /*
- 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, called instead of applicationWillTerminate: when the user quits.
- */
- int i = giAlarmInterval / kTimerInterval;
- while (i > 0)
- {
- DoorsBgTaskBegin();
- [NSThread sleepForTimeInterval:kTimerInterval];
- [self alarmFunc];
- DoorsBgTaskEnd();
-
- i--;
- }
- }
复制代码
4.3 闹钟代码,这个比较简单,打印一点信息就是了。
- - (void)alarmFunc
- {
- giAlarmInterval -= kTimerInterval;
-
- if (giAlarmInterval > 0)
- {
- NSLog(@"%d minutes left!", giAlarmInterval / 60);
- }
- else
- {
- NSLog(@"Alarm!Alarm!Alarm!");
-
- if ([alarmTimer isValid])
- {
- [alarmTimer invalidate];
- }
- }
- }
复制代码
/**********************Split line for coder*********************/
基本上就是这样子了,我这里只是希望给大家起到一点提示性的作用,大家如果能够有所收获就可以自由发挥,希望大家能开发出合理使用多任务的应用程序。
最后说明一点的是,官方推荐使用异步队列是方便大家把比较大的任务拆分成很多小的部分,同时放进队列里面去,提高效率的一种做法,如果用同步,有可能导致你要做的事情在10分钟内没做完,就只能被系统卡擦掉了。
●完整工程在此:
multitask_demo.zip (22.22 KB, 下载次数: 167)