iOS的后台任务

本文介绍了iOS应用在后台运行的各种模式,包括播放音频、实时定位更新、有限时长的任务、新闻杂志下载以及提供VoIP服务。文章详细讲解了每个模式的实现原理和代码示例,强调了应用后台模式的限制和苹果审核的要求。同时,通过一个示例项目演示了如何创建支持这些后台模式的应用。
摘要由CSDN通过智能技术生成
翻译自: http://www.raywenderlich.com/29948/backgrounding-for-ios


(代码部分若乱码,请移步原链接拷贝)

自ios4开始,用户点击home按钮时,你可以将app设计为挂起状态。app在内存中,除非用户再次返回到app,否则该app暂停运行。都是这种情况吗?


当然不是,在一些例外的情况下,app仍然可以在后台保持运行。这篇文章将介绍如何以及何时应用(几乎)所有这些后台操作功能。


应用后台运行模式实际上有很严格的限制条件,在ios上实现真正的多任务上,这并非一个奇特的解决办法。app退回后台时,比如用户切换到了另一个app,更多的是完全被挂起。

允许app在后台仍然运行的情况仅限于以下几种:

1,播放音频文件(playing audio)

2,获取定位更新(getting location updates)

3,杂志app中下载新的期刊(downloading new issues for newsstand apps)

4,VoIP 呼叫(handing VoIP calls)


假如你的app没有用到如上任一功能,那么非常不幸运。需要特别注意的是:针对所有的app,在它们真正被挂起之前,有最多10分钟的时间去完成其正在执行的任务。


所以后台模式可能并非如你所愿,但仍请看官细品!


接下来你将学习到,在IOS中5种基本的可用的后台模式。本章你创建的项目是一个基于tabbed的简单应用,每个tab展示了上述5种基本后台模式效果之一,即从‘播放音频’开始到‘接听Voice - over - IP’链接。


赘言不絮,开始正文。


初探--”后台模式“

在深入探讨之前,下面预览下这5种基本后台模式:

1,音频播放:在后台app依然可以播放/录制音频。

2,实时接收定位更新:app依然可以获取设备位置更新的回调。

3,执行一个有限时长的任务:通用的情况,在限定的时间内,app可以运行任意作用的代码。

4,杂志下载:对于杂志类app,允许其在后台下载更新的内容。

5,提供VoIP服务:允许app在后台运行任意作用的代码。当然前提是你的app必须提供了VoIP服务。


本文接下来将依次介绍上述5种后台模式,你若仅对其中的一种或几种模式感兴趣,可以选择阅读。


首先下载本文介绍的项目,本文demo下载链接:sample project   ,你也可以follow该项目的GitHub页面 ,其中有详尽的项目创建过程,尽管本文着重介绍的是后台模式的操作。

好消息:用户接口已经为你预先配置好了,这更有利于专注后台模式的学习。


运行项目,效果如下:


上面的tabs将是本节阐述的引导图。首先我们将进行音频后台模式。


附:为了达到测试的最优效果,你英爱在真机上运行本程序,因为有些后台功能在模拟器上不能阐释的那么完善(甚至完全没有效果)。


音频播放:

在IOS上有若干方法进行音频的播放,这些方法中的多数均要求继承回调来提供后续的音频数据进行播放。回调(如委托方法)即是在适当时间进行某种操作,使用音频流填充缓冲区。

假如打算以数据流方式来进行音频播放,你可以开启一个网络连接,并用该连接回调惊醒持续的数据流接受。


当使能音频后台模式播放时,既是app已经在后台,即不是当前活跃的app,IOS仍然可以调用上述回调方法---就是这样,这是本文介绍的后台模式中的4个之一,音频后台模式几乎是自动完成的,你仅需要激活它,并提供合适的基础操作即可。


仅当你的app是真的提供给用户音频播放功能,你才能使用音频后台模式。若我们抱有侥幸心理,为了获得CPU更多时间而利用该模式播放一段无声的音频,apple将会拒绝此类app。


本节,你将添加音频播放到项目,并开启后台模式演示其效果。


为了实现音频播放,首先需要学习AV Foundation,打开 TBFirstViewController.m文件,添加头文件:

  1. <span style="border:0px; font-family:inherit; font-size:16px; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(110,55,26)">#import <AVFoundation/AVFoundation.h></span>  


在viewDidLoad中,添加如下代码:

  1. <pre code_snippet_id="268909" snippet_file_name="blog_20140401_1_9508045" name="code" class="objc">// Set AVAudioSession  
  2.     NSError *sessionError = nil;  
  3.     [[AVAudioSession sharedInstance] setDelegate:self];  
  4.     [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];  
  5.    
  6.     // Change the default output audio route  
  7.     UInt32 doChangeDefaultRoute = 1;  
  8.     AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,  
  9.       sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);</pre>  

 

代码初始化了audio session对象,并确定用扬声器来播放而非听筒。

以下变量用来跟踪播放进程:

  1. @property (nonatomic, strong) AVQueuePlayer *player;  
  2. @property (nonatomic, strong) id timeObserver;  


声明在如下位置:

  1. <pre code_snippet_id="268909" snippet_file_name="blog_20140401_3_7916835" name="code" class="objc">@interface TBFirstViewController ()  
  2.    
  3. // Insert code here  
  4.    
  5. @end</pre><br><br>  

 


项目开始包含了一个音频文件,来自favorit rotalty-free music websites 。你可以免费使用其中的音频文件,所有的文件由Kevin Macleod 提供,所以,感谢Kevin!


在IOS上播放音乐,一种最简单的方法之一即是应用AV Foundations AVPlayer。 故我们的实例将会使用一个AVPlayer的子类叫做AVQueuePlayer 。AVQueuePlayer允许我们设置一个AVPlayerItems队列,用来依次并自动的播放音频文件。


在viewDidLoad结尾:


  1.  <pre code_snippet_id="268909" snippet_file_name="blog_20140401_4_5899731" name="code" class="objc">NSArray *queue = @[  
  2.     [AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:@"IronBacon" withExtension:@"mp3"]],  
  3.     [AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:@"FeelinGood" withExtension:@"mp3"]],  
  4.     [AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:@"WhatYouWant" withExtension:@"mp3"]]];  
  5.    
  6.     self.player = [[AVQueuePlayer alloc] initWithItems:queue];  
  7.     self.player.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;  
  8. </pre><br>  

代码首先创建了一个含有AVPlayerItems对象的数组,接着以该数组初始化AVQueuePlayer对象,并设置其为连续播放。


在播放进程中,为了更新音乐名字,你需要注册监听player的currentItem属性,该功能代码添加至viewDidLoad的结尾:

  1. <pre code_snippet_id="268909" snippet_file_name="blog_20140401_5_8342729" name="code" class="objc"> [self.player addObserver:self  
  2.                   forKeyPath:@"currentItem"  
  3.                      options:NSKeyValueObservingOptionNew  
  4.                      context:nil];</pre><br><br>  

 

当player的currentItem属性变化时,将会回调监听事件。添加监听事件到viewDidLoad下面:

  1. <pre code_snippet_id="268909" snippet_file_name="blog_20140401_6_1309116" name="code" class="objc">- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(voidvoid *)context  
  2. {  
  3.     if ([keyPath isEqualToString:@"currentItem"])  
  4.     {  
  5.         AVPlayerItem *item = ((AVPlayer *)object).currentItem;  
  6.         self.lblMusicName.text = ((AVURLAsset*)item.asset).URL.pathComponents.lastObject;  
  7.         NSLog(@"New music name: %@", self.lblMusicName.text);  
  8.     }  
  9. }  
  10. </pre><br><br>  

当监听方法被调用时,首先应该确定的是更新的属性是我们需要关注的。但在本例中,因为仅监听一个属性,故判断语句不是必须的。但是判断检测是一个很好的习惯,也是以防后期会添加另外的属性监听。如是‘currentItem’属性变化,测更新lb1MusicName标签。


你或许需要更新当前播放条目的已播时间,实现的最好方法是利用:addPeriodicTimeObserverForInterval:queue:usingBlock:方法,在指定的queue中提供回调block。

在viewDidLoad结尾添加:

  1. <pre code_snippet_id="268909" snippet_file_name="blog_20140401_7_5703962" name="code" class="objc">void (^observerBlock)(CMTime time) = ^(CMTime time) {  
  2.         NSString *timeString = [NSString stringWithFormat:@"%02.2f", (float)time.value / (float)time.timescale];  
  3.         if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) {  
  4.             self.lblMusicTime.text = timeString;  
  5.         } else {  
  6.             NSLog(@"App is backgrounded. Time is: %@", timeString);  
  7.         }  
  8. };  
  9.    
  10.     self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(10, 1000)  
  11.                                                                   queue:dispatch_get_main_queue()  usingBlock:observerBlock];</pre><br><br>  

 

首先创建一个block,当时间更新时,它将被调用。假如你对block不熟悉,可以阅读:How to User Blocks in IOS5 tutorial 。该block基于app的状态,创建一个显示音乐播放时间的字串。

在此之后,便是调用-(id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void(^)(CMTime time))block 开始获取更新信息。


附:关于app的状态

你的app始终处在以下5种状态的其中之一,概要如下:

Not running:app在启动前在此状态

Active:一旦app开启,即此状态

Inactive:app在运行时有事件中断它的执行,比如一个电话呼叫到来,它将进入该状态。inactive意味着app仍然在前台运行但却不接收事件。

Backgrounded:此状态,app不在前台,但其仍能执行代码

Suspended:当不执行代码时,app即进入该状态


若你希望了解上述状态的更详尽信息,请移步apple官网:App States and Multitasking


通过调用[[UIApplication sharedApplication]applicationState]来检测app的状态,不过要注意的是你仅可以获取以下3种状态之一:

UIApplicationStateActive;UIApplicationStateInactive;UIApplicationStateBackground。suspended和not running很明显不可能在运行代码期间检测到,所以没有它们无对应的值。


返回代码,加入app处在active状态,你需要更新label标签,否则,将更新信息打印到控制台即可。在后台状态时仍需要更新label标签,但现在需要演示的是app进入后台后,仍能接收回调。


现在要做的是完成play/pause按钮的工作,即实现didTapPlayPause方法,添加该方法至TBFirstViewController.m中:

[objc]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值