1.什么是ANCS?
ANCS是Apple Notification Center Service的簡稱,中文為蘋果通知中心服務(wù)落包。
ANCS是蘋果讓周邊藍(lán)牙設(shè)備(手環(huán)、手表等)可以通過低功耗藍(lán)牙訪問IOS設(shè)備(iphone摊唇、ipad等)上的各類通知提供的一種簡單方便的機(jī)制咐蝇。
ANCS是基于BLE協(xié)議中的通用屬性協(xié)議(Generic Attribute Profile,GATT)協(xié)議實(shí)現(xiàn)的,他是GATT協(xié)議的一個(gè)子集巷查。在ANCS協(xié)議中有序,IOS設(shè)備作為gatt-server,而周邊設(shè)備作為gatt client來連接和使用server提供的其他services。
詳細(xì)的可以參考官網(wǎng)上的解釋ANCS spec
2.帶來的影響是什么岛请?
目前iOS這邊關(guān)于藍(lán)牙的開發(fā)大多都是基于Ble來實(shí)現(xiàn)的旭寿,對(duì)連接斷開有較深使用經(jīng)驗(yàn)的人都知道,iOS藍(lán)牙這邊的Ble關(guān)于斷連重新連接模塊都是用的懶加載(再次連上同一個(gè)mac地址的ble設(shè)備崇败,底層沒有重新獲取特征值盅称,只是從緩存里讀了一份給到上層)
所以ANCS帶來的影響是:
- 1.無法從代碼層面斷開BLE連接了。
既然無法斷開后室,那就意味著一旦連上了就無法讓別的手機(jī)去搜索到缩膝,而且殺死當(dāng)前連上的應(yīng)用后轴脐,也無法通過常規(guī)的手段(centerManager.scanForPeripherals(withServices: nil, options: nil))去搜索到設(shè)備义图。
- 2.業(yè)務(wù)流程設(shè)計(jì)上變得用戶體驗(yàn)很糟糕
3.如何解決?
- 1.目前除了手動(dòng)解除綁定以外卵史,沒有其他的方法可以解除綁定
- 2.iOS提供了 **central.retrievePeripherals(withIdentifiers:[uuid]) **的方法來獲取已連接上的ANCS設(shè)備
以下提供了一個(gè)demo來感受一下整個(gè)過程:
import Foundation
import CoreBluetooth
class BLEManager: NSObject,CBCentralManagerDelegate,CBPeripheralDelegate{
lazy var centerManager:CBCentralManager = {
CBCentralManager(delegate: self, queue: DispatchQueue.main)
}()
var myPeripheral:CBPeripheral?
private var list:[CBPeripheral] = []
override init() {
super.init()
}
func start(){
centerManager.scanForPeripherals(withServices: nil, options: nil)
print("?? Need to hasPerfix your ble device's name or it maybe found nothing")
DispatchQueue.main.asyncAfter(deadline: .now()+2, execute: {
if let item = self.list.first {
if item.state == .disconnected {
self.centerManager.connect(item, options: nil)
}
}
})
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unknown:
break
case .resetting:
break
case .unsupported:
break
case .unauthorized:
break
case .poweredOff:
break
case .poweredOn:
centerManager.scanForPeripherals(withServices: nil, options: nil)
@unknown default:
break
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if ((peripheral.name?.hasPrefix("AC701N_")) != nil){
if peripheral.name == "AC701N_GJ" {
centerManager.stopScan()
list.append(peripheral)
print("Did found:\n\(peripheral)")
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
self.myPeripheral = peripheral
self.myPeripheral?.delegate = self
self.myPeripheral?.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("disconnect Ble in this application !")
print("The Ble ANCS device is alway here,you can watch it in system setting")
let uuid = UUID(uuid: peripheral.identifier.uuid)
let array = central.retrievePeripherals(withIdentifiers:[uuid])
for item in array {
print("Now the ble is founded in here:\n\(item)")
DispatchQueue.main.asyncAfter(deadline: .now()+10) {
print("Here is to reconnect by 10 sec.")
self.centerManager.connect(item, options: nil)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else{
return
}
for service in services {
if service.uuid.uuidString == "AE00" {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let charts = service.characteristics else { return }
for chart in charts {
if chart.uuid.uuidString == "AE01" {
print("get wirte chart");
}
if chart.uuid.uuidString == "AE02" {
print("get read chart")
}
}
print("Please make sure pair in your phone ,it will disconnect by 10 sec.")
DispatchQueue.main.asyncAfter(deadline: .now()+10) {
print("go to cancel connect BLE")
self.centerManager.cancelPeripheralConnection(peripheral)
}
}
}
運(yùn)行起來:
class ViewController: UIViewController {
var mgr = BLEManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
mgr.start()
}
}
log分析
2022-04-01 12:00:37.131809+0800 BleConnect[683:73634] [CoreBluetooth] API MISUSE: <CBCentralManager: 0x282755320> can only accept this command while in the powered on state
?? Need to hasPerfix your ble device's name or it maybe found nothing
Did found:
<CBPeripheral: 0x2822540b0, identifier = A790557A-3B84-644B-B3AF-E8AC733D7089, name = AC701N_GJ, mtu = 0, state = disconnected>
get wirte chart
get read chart
Please make sure pair in your phone ,it will disconnect by 10 sec.
go to cancel connect BLE
disconnect Ble in this application !
The Ble ANCS device is alway here,you can watch it in system setting
Now the ble is founded in here:
<CBPeripheral: 0x2822540b0, identifier = A790557A-3B84-644B-B3AF-E8AC733D7089, name = AC701N_GJ, mtu = 23, state = disconnected>
Here is to reconnect by 10 sec.
get wirte chart
get read chart
Please make sure pair in your phone ,it will disconnect by 10 sec.
go to cancel connect BLE
disconnect Ble in this application !
The Ble ANCS device is alway here,you can watch it in system setting
Now the ble is founded in here:
<CBPeripheral: 0x2822540b0, identifier = A790557A-3B84-644B-B3AF-E8AC733D7089, name = AC701N_GJ, mtu = 23, state = disconnected>
最后注意的點(diǎn)
- 1.設(shè)備通過A應(yīng)用連接手機(jī)贡避,A應(yīng)用內(nèi)無法通過API直接斷開設(shè)備痛黎,當(dāng)殺死應(yīng)用后,設(shè)備會(huì)處于一直連接著手機(jī)的情況刮吧。
這個(gè)時(shí)候只能通過A應(yīng)用retrieveconnectedperipher去搜索出來舅逸,重新進(jìn)行連接。 - 2.在情景1的A應(yīng)用殺死時(shí)設(shè)備還處于連接手機(jī)系統(tǒng)皇筛,B應(yīng)用/其他應(yīng)用通過retrieveconnectedperipher搜索出來設(shè)備琉历,但不可以獲取設(shè)備的service。
- 3.在同一應(yīng)用,同一次連接BLE下旗笔,如果設(shè)備Mac不變彪置,短期內(nèi)多次發(fā)起斷連請(qǐng)求,iOS系統(tǒng)不會(huì)去重復(fù)請(qǐng)求設(shè)備的Service蝇恶,讀到的僅僅是緩存拳魁。