原文网址:http://southpeak.github.io/blog/2014/07/31/core-bluetoothkuang-jia-zhi-er-:hou-tai-chu-li/

在开发BLE相关应用时,由于应用在后台时会有诸多资源限制,需要考虑应用的后台处理问题。默认情况下,当程序位于后台或挂起时,大多数普通的Core Bluetooth任务都无法使用,不管是Central端还是Peripheral端。但我们可以声明我们的应用支持Core Bluetooth后台执行模式,以允许程序从挂起状态中被唤醒以处理蓝牙相关的事件。

然而,即使我们的应用支持两端的Core Bluetooth后台执行模式,它也不能一直运行。在某些情况下,系统可能会关闭我们的应用来释放内存,以为当前前台的应用提供更多的内存空间。在iOS7及后续版本中,Core Bluetooth支持保存Central及Peripheral管理器对象的状态信息,并在程序启动时恢复这些信息。我们可以使用这个特性来支持与蓝牙设备相关的长时间任务。

下面我们将详细讨论下这些问题。

只支持前台操作(Foreground-Only)的应用

大多数应用在进入到后台后都会在短时间内进入挂起状态,除非我们请求执行一些特定的后台任务。当处理挂起状态时,我们的应用无法继续执行蓝牙相关的任务。

在Central端,Foreground-Only应用在进入后台或挂起时,无法继续扫描并发现下在广告的Peripheral设备。而在Peripheral端,无法广告自身,同时Central端对其的任何访问操作都会返回一个错误。

Foreground-Only应用挂起时,所有蓝牙相关的事件都会被系统放入一个队列,当应用进入前台后,系统会将这些事件发送给我们的应用。也就是说,当某些事件发生时,Core Bluetooth提供了一种方法来提示用户。用户可以使用这些提示来决定是否将应用放到前台。在《Core Bluetooth框架之一:Central与Peripheral》中我们介绍了connectPeripheral:options:方法,在调用这个方法时,我们可以设备options参数来设置这些提示:

  1. CBConnectPeripheralOptionNotifyOnConnectionKey:当应用挂起时,如果有一个连接成功时,如果我们想要系统为指定的peripheral显示一个提示时,就使用这个key值。
  2. CBConnectPeripheralOptionNotifyOnDisconnectionKey:当应用挂起时,如果连接断开时,如果我们想要系统为指定的peripheral显示一个断开连接的提示时,就使用这个key值。
  3. CBConnectPeripheralOptionNotifyOnNotificationKey:当应用挂起时,使用该key值表示只要接收到给定peripheral端的通知就显示一个提示。

Core Bluetooth后台执行模式

我们可以在Info.plist文件中设置Core Bluetooth后台执行模式,以让应用支持在后台执行一些蓝牙相关的任务。当应用声明了这一功能时,系统会将应用唤醒以允许它处理蓝牙相关的任务。这个特性对于与那种定时发送数据的BLE交互的应用非常有用。

有两种Core Bluetooth后台执行模式,一种用于实现Central端操作,一种用于实现Peripheral端操作。如果我们的应用同时实现了这两端的功能,则需要声明同时支持两种模式。我们需要在Info.plist文件添加UIBackgroundModes键,同时添加以下两个值或其中之一:

  1. bluetooth-central(App communicates using CoreBluetooth)
  2. bluetooth-peripheral(App shares data using CoreBluetooth)

bluetooth-central模式

如果设置了bluetooth-central值,则我们的应用在后台时,仍然可以查找并连接到Peripheral设备,以及查找相应数据。另外,系统会在CBCentralManagerDelegate或CBPeripheralDelegate代理方法被调用时唤醒我们的应用,允许应用处理事件,如建立连接或断开连接等等。

虽然应用在后台时,我们可以执行很多蓝牙相关任务,但需要记住应用在前后台扫描Peripheral设备时还是不一样的。当我们的应用在后台扫描Peripheral设备时,

  1. CBCentralManagerScanOptionAllowDuplicatesKey扫描选项会被忽略,同一个Peripheral端的多个发现事件会被聚合成一个发现事件。
  2. 如果扫描Peripheral设备的多个应用都在后台,则Central设备扫描广告数据的时间间隔会增加。结果是发现一个广告的Peripheral设备可能需要很长时间。

这些处理在iOS设备中最小化无线电的使用及改善电池的使用寿命非常有用。

bluetooth-peripheral模式

如果设置了bluetooth-peripheral值,则我们的应用在后台时,应用会被唤醒以处理来自于连接的Central端的读、写及订阅请求,Core Bluetooth还允许我们在后台进行广告。与Central端类似,也需要注意前后台的操作区别。特别是在广告时,有以下几点区别:

  1. CBAdvertisementDataLocalNameKey广告key值会被忽略,Peripheral端的本地名不会被广告
  2. CBAdvertisementDataServiceUUIDsKey键的所有服务的UUID都被放在一个”overflow”区域中,它们只能被那些显示要扫描它们的网络设备发现。
  3. 如果多个应用在后台广告,则Peripheral设备发送广告包的时间间隔会变长。

在后台执行长(Long-Term)任务

虽然建议尽快完成后台任务,但有些应该仍然需要使用Core Bluetooth来执行一个长任务。这时就涉及到状态的保存与恢复操作。

状态保存与恢复

因为状态保存与恢复是内置于Core Bluetooth的,我们的程序可以选择这个特性,让系统保存Central和Peripheral管理器的状态并继续执行一些蓝牙相关的任务,即使此时程序不再运行。当这些任务中的一个完成时,系统会在后台重启程序,程序可以恢复先前的状态以处理事件。Core Bluetooth支持Central端、Peripheral端的状态保存与恢复,也可以同时支持两者。

在Central端,系统会在关闭程序释放内存时保存Central管理器对象的状态(如果有多个Central管理器,我们可以选择系统跟踪哪个管理器)。对于给定的CBCentralManager对象,系统会跟踪如下信息:

  1. Central管理器扫描的服务
  2. Central管理器尝试或已经连接的Peripheral
  3. Central管理器订阅的特性

在Peripheral端,对于给定的CBPeripheralManager对象,系统会跟踪以下信息:

  1. Peripheral管理器广告的数据
  2. Peripheral管理器发布到设备数据库的服务和特性
  3. 订阅Peripheral管理器的特性值的Central端

当系统将程序重启到后台后,我们可以重新重新初始化我们程序的Central和Peripheral管理器并恢复状态。我们接下来将详细介绍一下如何使用状态保存与恢复。

添加状态保存和恢复支持

Core Bluetooth中的状态保存与恢复是可选的特性,需要程序的支持才能工作。我们可以按照以下步骤来添加这一特性的支持:

  1. (必要步骤)当分配及初始化Central或Peripheral管理器对象时,选择性加入状态保存与恢复。
  2. (必要步骤)在系统重启程序时,重新初始化Central或Peripheral管理器对象
  3. (必要步骤)实现适当的恢复代理方法
  4. (可选步骤)更新Central或Peripheral管理器初始化过程
选择性加入状态保存与恢复

为了选择性加入状态保存与恢复特性,在分配及初始化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管理器对象

当系统重启程序到后台后,我们所需要做的第一件事就是使用恢复标识来重新初始化这些对象。如果我们的应用只有一个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设备的内置无线电,而无线电的使用会影响到电池的寿命,所以尽量减少在后台执行的任务。任何会被蓝牙相关任务唤醒的应用应该尽快处理任务并在完成时重新挂起。

下面是一些基础的准则:

  1. 应用应该是基于会话的,并提供接口以允许用户决定什么时候开始及结束蓝牙相关事件的分发。
  2. 一旦被唤醒,一个应用大概有10s的时间来完成任务。理想情况下,应用应该尽快完成任务并重新挂起。系统可以对执行时间太长的后台任务进行限流甚至杀死。
  3. 应用被唤醒时,不应该执行一些无关紧要的操作。

【转】Core Bluetooth框架之二:后台处理的更多相关文章

  1. 【转】Core Bluetooth框架之一:Central与Peripheral

    原文网址:http://southpeak.github.io/blog/2014/07/29/core-bluetoothkuang-jia-zhi-%5B%3F%5D-:centralyu-per ...

  2. 创造DotNet Core轻量级框架【二】

    上一篇 创造DotNet Core轻量级框架[一] 10 题外话 上一篇文章感谢大家提出的各种小建议和各种讨论,但是在写文章的时候框架最最最基础的样子已经做出来了,所以大家的各种建议和意见我会在后续逐 ...

  3. Uwl.Admin.Core开源框架(二) 使用QuartzNet

    Uwl.Admin.Core中使用QuartzNet定时任务模块: 本文负责讲解RabbitMQ的使用 Uwl.Admin.Core使用的技术有: *.Async和Await 异步编程 *.Repos ...

  4. iOS 上的蓝牙框架 - Core Bluetooth for iOS

    原文: Core Bluetooth for iOS 6 Core Bluetooth 是在iOS5首次引入的,它允许iOS设备可以使用健康,运动,安全,自动化,娱乐,附近等外设数据.在iOS 6 中 ...

  5. Core BlueTooth官方文档翻译

    本⽂文是苹果<Core Bluetooth Programming Guide>的翻译. 关于Core Bluetooth Core Bluetooth 框架提供了蓝⽛牙低功耗⽆无线设备与 ...

  6. Core Bluetooth 概述 【官方文档翻译】

    Core Bluetooth 框架在Mac和iOS平台,为配备了低功耗蓝牙无线技术的设备提供了进行通信所需要的类.例如,您的应用程序可以发现,探索,和低功耗的外围设备进行交互,如心率监视器.数字温控器 ...

  7. core Bluetooth(蓝牙4.0)

    蓝牙4.0以低功耗著称,一般也叫BLE(Bluetooth Low Energy). 目前主要应用的场景有:智能家居.运动手环和室内导航等. 利用core Bluetooth框架可以实现苹果设备与第三 ...

  8. 一步一步实现web程序信息管理系统之二----后台框架实现跳转登陆页面

    SpringBoot springboot的目的是为了简化spring应用的开发搭建以及开发过程.内部使用了特殊的处理,使得开发人员不需要进行额外繁锁的xml文件配置的编写,其内部包含很多模块的配置只 ...

  9. ASP.NET Core 6框架揭秘实例演示[21]:如何承载你的后台服务

    借助 .NET提供的服务承载(Hosting)系统,我们可以将一个或者多个长时间运行的后台服务寄宿或者承载我们创建的应用中.任何需要在后台长时间运行的操作都可以定义成标准化的服务并利用该系统来承载,A ...

随机推荐

  1. nodejs -- crypto MD5签名

    MD5使用方法: const crypto = require('crypto'); var obj = crypto.createHash('md5'); // 可多次调用 update obj.u ...

  2. (转)ORA-01502

    问题:ora-01502 索引或这类索引的分区处于不可用状态 引发:移动数据表分区,导致索引失效 解决:重建失效索引 1. select index_name ,status  from user_i ...

  3. 使用Storm实现实时大数据分析!

    随着数据体积的越来越大,实时处理成为了许多机构需要面对的首要挑战.Shruthi Kumar和Siddharth Patankar在Dr.Dobb's上结合了汽车超速监视,为我们演示了使用Storm进 ...

  4. Django 中的 csrf_token 与单元测试

    Django 中的 csrf_token 与单元测试 在<Python Web开发:测试驱动方法>一书中作者使用的 Django 版本是 1.7,而我使用的是1.9.7版(官网已经更新到1 ...

  5. JS棋盘

    有一个棋盘,有64个方格,在第一个方格里面放1粒芝麻重量是0.00001kg, 第二个里面放2粒,第三个里面放4,棋盘上放的所有芝麻的重量 <!DOCTYPE html> <html ...

  6. elasticsearch批量操作

    1.批量查询的好处 就是一条一条的查询,比如说要查询100条数据,那么就要发送100次网络请求,这个开销还是很大的 如果进行批量查询的话,查询100条数据,就只要发送1次网络请求,网络请求的性能开销缩 ...

  7. hibernate中session的get和load方法的区别和联系:

    1. get:及时加载,调用到get方法时立即向数据库查询(在没有session缓存的请况). 2. load:默认使用懒加载,当用到数据的时候才向数据库查询(在没有session缓存的请况). 3. ...

  8. GDI Bezier 样条曲线(7)

    Bezier 样条曲线 Bezier 样条曲线使用四个点来定义:两个端点(起点和终点)和两个控点(用于使其不同程度地弯曲). 绘制 Bezier 样条曲线 使用 PolyBezier 函数和 Poly ...

  9. LIS,LCS,LICS 学习笔记

    1.最长上升子序列(LIS) 子序列: 1.可以不连续 2.相对位置不变 dp[i][j] 表示前i位置,最大值为j的LIS长度 1. dp[i-1][j] 前i-1位置,最大值为j的LIS长度 (没 ...

  10. P3375 【模板】KMP字符串匹配 (KMP模板)

    题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next. (如果你不知道这是什么意思也不要问,去百度 ...