iOS对于ANCS设备的处理

1.什么是ANCS?

ANCS是Apple Notification Center Service的简称,中文为苹果通知中心服务。
ANCS是苹果让周边蓝牙设备(手环、手表等)可以通过低功耗蓝牙访问IOS设备(iphone、ipad等)上的各类通知提供的一种简单方便的机制。

ANCS是基于BLE协议中的通用属性协议(Generic Attribute Profile,GATT)协议实现的,他是GATT协议的一个子集。在ANCS协议中,IOS设备作为gatt-server,而周边设备作为gatt client来连接和使用server提供的其他services。

详细的可以参考官网上的解释ANCS spec

2.带来的影响是什么?

目前iOS这边关于蓝牙的开发大多都是基于Ble来实现的,对连接断开有较深使用经验的人都知道,iOS蓝牙这边的Ble关于断连重新连接模块都是用的懒加载(再次连上同一个mac地址的ble设备,底层没有重新获取特征值,只是从缓存里读了一份给到上层)

所以ANCS带来的影响是:

  • 1.无法从代码层面断开BLE连接了。

既然无法断开,那就意味着一旦连上了就无法让别的手机去搜索到,而且杀死当前连上的应用后,也无法通过常规的手段(centerManager.scanForPeripherals(withServices: nil, options: nil))去搜索到设备。

  • 2.业务流程设计上变得用户体验很糟糕

3.如何解决?

  • 1.目前除了手动解除绑定以外,没有其他的方法可以解除绑定
  • 2.iOS提供了 **central.retrievePeripherals(withIdentifiers:[uuid]) **的方法来获取已连接上的ANCS设备

以下提供了一个demo来感受一下整个过程:

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)
        }
    }
}

运行起来:

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>

最后注意的点

  • 1.设备通过A应用连接手机,A应用内无法通过API直接断开设备,当杀死应用后,设备会处于一直连接着手机的情况。
    这个时候只能通过A应用retrieveconnectedperipher去搜索出来,重新进行连接。
  • 2.在情景1的A应用杀死时设备还处于连接手机系统,B应用/其他应用通过retrieveconnectedperipher搜索出来设备,但不可以获取设备的service。
  • 3.在同一应用,同一次连接BLE下,如果设备Mac不变,短期内多次发起断连请求,iOS系统不会去重复请求设备的Service,读到的仅仅是缓存。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容