原文网址:http://southpeak.github.io/blog/2014/07/31/core-bluetoothkuang-jia-zhi-er-:hou-tai-chu-li/git
在开发BLE相关应用时,因为应用在后台时会有诸多资源限制,须要考虑应用的后台处理问题。默认状况下,当程序位于后台或挂起时,大多数普通的Core Bluetooth任务都没法使用,不论是Central端仍是Peripheral端。但咱们能够声明咱们的应用支持Core Bluetooth后台执行模式,以容许程序从挂起状态中被唤醒以处理蓝牙相关的事件。github
然而,即便咱们的应用支持两端的Core Bluetooth后台执行模式,它也不能一直运行。在某些状况下,系统可能会关闭咱们的应用来释放内存,觉得当前前台的应用提供更多的内存空间。在iOS7及后续版本中,Core Bluetooth支持保存Central及Peripheral管理器对象的状态信息,并在程序启动时恢复这些信息。咱们可使用这个特性来支持与蓝牙设备相关的长时间任务。数据库
下面咱们将详细讨论下这些问题。网络
大多数应用在进入到后台后都会在短期内进入挂起状态,除非咱们请求执行一些特定的后台任务。当处理挂起状态时,咱们的应用没法继续执行蓝牙相关的任务。并发
在Central端,Foreground-Only应用在进入后台或挂起时,没法继续扫描并发现下在广告的Peripheral设备。而在Peripheral端,没法广告自身,同时Central端对其的任何访问操做都会返回一个错误。app
Foreground-Only应用挂起时,全部蓝牙相关的事件都会被系统放入一个队列,当应用进入前台后,系统会将这些事件发送给咱们的应用。也就是说,当某些事件发生时,Core Bluetooth提供了一种方法来提示用户。用户可使用这些提示来决定是否将应用放到前台。在《Core Bluetooth框架之一:Central与Peripheral》中咱们介绍了connectPeripheral:options:方法,在调用这个方法时,咱们能够设备options参数来设置这些提示:框架
咱们能够在Info.plist文件中设置Core Bluetooth后台执行模式,以让应用支持在后台执行一些蓝牙相关的任务。当应用声明了这一功能时,系统会将应用唤醒以容许它处理蓝牙相关的任务。这个特性对于与那种定时发送数据的BLE交互的应用很是有用。ide
有两种Core Bluetooth后台执行模式,一种用于实现Central端操做,一种用于实现Peripheral端操做。若是咱们的应用同时实现了这两端的功能,则须要声明同时支持两种模式。咱们须要在Info.plist文件添加UIBackgroundModes键,同时添加如下两个值或其中之一:代理
若是设置了bluetooth-central值,则咱们的应用在后台时,仍然能够查找并链接到Peripheral设备,以及查找相应数据。另外,系统会在CBCentralManagerDelegate或CBPeripheralDelegate代理方法被调用时唤醒咱们的应用,容许应用处理事件,如创建链接或断开链接等等。rest
虽然应用在后台时,咱们能够执行不少蓝牙相关任务,但须要记住应用在先后台扫描Peripheral设备时仍是不同的。当咱们的应用在后台扫描Peripheral设备时,
这些处理在iOS设备中最小化无线电的使用及改善电池的使用寿命很是有用。
若是设置了bluetooth-peripheral值,则咱们的应用在后台时,应用会被唤醒以处理来自于链接的Central端的读、写及订阅请求,Core Bluetooth还容许咱们在后台进行广告。与Central端相似,也须要注意先后台的操做区别。特别是在广告时,有如下几点区别:
虽然建议尽快完成后台任务,但有些应该仍然须要使用Core Bluetooth来执行一个长任务。这时就涉及到状态的保存与恢复操做。
由于状态保存与恢复是内置于Core Bluetooth的,咱们的程序能够选择这个特性,让系统保存Central和Peripheral管理器的状态并继续执行一些蓝牙相关的任务,即便此时程序再也不运行。当这些任务中的一个完成时,系统会在后台重启程序,程序能够恢复先前的状态以处理事件。Core Bluetooth支持Central端、Peripheral端的状态保存与恢复,也能够同时支持二者。
在Central端,系统会在关闭程序释放内存时保存Central管理器对象的状态(若是有多个Central管理器,咱们能够选择系统跟踪哪一个管理器)。对于给定的CBCentralManager对象,系统会跟踪以下信息:
在Peripheral端,对于给定的CBPeripheralManager对象,系统会跟踪如下信息:
当系统将程序重启到后台后,咱们能够从新从新初始化咱们程序的Central和Peripheral管理器并恢复状态。咱们接下来将详细介绍一下如何使用状态保存与恢复。
Core Bluetooth中的状态保存与恢复是可选的特性,须要程序的支持才能工做。咱们能够按照如下步骤来添加这一特性的支持:
为了选择性加入状态保存与恢复特性,在分配及初始化Central或Peripheral管理器时提供一个一个惟一恢复标识。恢复标识是一个字条串,用来让Core Bluetooth和程序标识Central或Peripheral管理器。字符串的值只在本身的代码中有意义,但这个字符串告诉Core Bluetooth咱们须要保存对象的状态。Core Bluetooth只保存那些有恢复标识的对象。
例如,在实现Central端时,为了选择性加入状态保存与恢复特性,在初始化CBCentralManager对象时,能够指定初始化选项CBCentralManagerOptionRestoreIdentifierKey,并提供一个恢复标识,以下代码所示:
centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionRestoreIdentifierKey: @"restoreIdentifier"}];
实现Peripheral端时的操做也相似,只不过咱们使用选项CBPeripheralManagerOptionRestoreIdentifierKey键。
由于程序能够有多个Central或Peripheral管理器,因此须要确保恢复标识是惟一的,这样系统能够区分这些管理器对象。
当系统重启程序到后台后,咱们所须要作的第一件事就是使用恢复标识来从新初始化这些对象。若是咱们的应用只有一个Central管理器或Peripheral管理器,且管理器在程序的整个生命周期都存在,则后续咱们便不须要再作更多的处理。但若是咱们有多个管理器,或者管理器不是存在于程序的整个生命周期,则系统重启应用时,咱们须要知道从新初始化哪个管理器。咱们能够经过在程序代理对象的application:didFinishLaunchingWithOptions:方法中,使用合适的启动选项键来访问管理器对象的列表(这个列表是程序关闭是系统为程序保存的)。
下面代码展现了程序从新启动时,咱们获取全部Central管理器对象的恢复标识:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. NSArray *centralManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey]; // TODO: ... return YES; }
有了这个恢复标识的列表后,咱们就能够从新初始化咱们所须要的管理器对象了。
从新初始化Central或Peripheral管理器对象后,咱们经过使用蓝牙系统的状态同步这些对象的状态来恢复它们。此时,咱们须要实现一些恢复代理方法。对于Central管理器,咱们实现centralManager:willRestoreState:代理方法;对于Peripheral管理器管理器,咱们实现peripheralManager:willRestoreState:代理方法。
对于选择性加入保存与恢复特性的应用来讲,这些方法是程序启动到后台以完成一些蓝牙相关任务所调用的第一个方法。而对于非选择性加入特性的应用来讲,会首先调用centralManagerDidUpdateState:和peripheralManagerDidUpdateState:代理方法。
在上述两个代理方法中,最后一个参数是一个字典,包含程序关闭时保存的关于管理器的信息。以下代码所示,咱们可使用CBCentralManagerRestoredStatePeripheralsKey键来获取Central管理器已链接的或尝试链接的全部Peripheral设备的列表:
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary *)state { NSArray *peripherals = state[CBCentralManagerRestoredStatePeripheralsKey]; // TODO: ... }
获取到这个列表后,咱们即可以根据须要来作处理。
在实现了前面的三个步骤后,咱们可能想更新咱们的管理器的初始化过程。虽然这一步是可选的,但若是要确认任务是否运行正常时,很是有用。例如,咱们的程序可能在解析所链接的Peripheral设备的数据的过程当中被关闭。当程序使用这个Peripheral设备做恢复操做时,没法知道数据处理到哪了。咱们须要确保程序从数据操做中止的位置继续开始操做。
又以下面的代码展现了在centralManagerDidUpdateState:代理方法中初始化程序操做时,咱们能够找出是否成功发现了被恢复的Peripheral设备的指定服务:
NSUInteger serviceUUIDIndex = [peripheral.services indexOfObjectPassingTest:^BOOL(CBService *obj, NSUInteger index, BOOL *stop) { return [obj.UUID isEqual:myServiceUUIDString]; }]; if (serviceUUIDIndex == NSNotFound) { [peripheral discoverServices:@[myServiceUUIDString]]; ... }
如上例所述,若是系统在程序完成搜索服务时关闭了应用,则经过调用discoverServices:方法在关闭的那个点开始解析恢复的Peripheral数据。若是程序成功搜索到服务,咱们能够确认是否搜索到正确的特性。经过更新初始化过程,咱们能够确保在正确的时间调用正确的方法。
虽然咱们可能须要声明应用支持Core Bluetooth后台执行模式,以完成特定的任务,但老是应该慎重考虑执行后台操做。由于执行太多的蓝牙相关任务须要使用iOS设备的内置无线电,而无线电的使用会影响到电池的寿命,因此尽可能减小在后台执行的任务。任何会被蓝牙相关任务唤醒的应用应该尽快处理任务并在完成时从新挂起。
下面是一些基础的准则: