iOS蓝牙开发小结

接触iOS蓝牙框架(CoreBluetooth)有一年多的时间了,遇到过很多问题,最近有空就想着记录几个自己觉得比较重要的知识点。

  1. 同一个iphone在搜索时对不同的蓝牙设备会生成不同的UUID(universally unique identifier),当更换iphone去连接同一个设备时,需要重新搜索获得新的UUID才能连接(调用下面的方法,传入目的设备的UUID,如果得到的数组为空就需要重新搜索设备才能连接)
  • (NSArray>)retrievePeripheralsWithIdentifiers:(NSArray>)identifiers NS_AVAILABLE(NA, 7_0);
    
    
  1. 不是连接过某个蓝牙设备就表示iphone已记住这个设备(retrievePeripheralsWithIdentifiers:返回有效值,下一次可以快速重新连接),只有跟设备有过读写交互才会记住这个设备;

you cannot initiate pairing from the iOS central side. Instead, you have to read/write a characteristic value,and then let your peripheral respond with an "Insufficient Authentication" error.iOS will then initiate pairing, will store the keys for later use (bonding) and encrypts the link. As far as I know,it also caches discovery information, so that future connections can be set up faster.

  1. iOS8.0以上断开手机蓝牙时,会先响应CBCentralManagerDelegate的蓝牙连接断开代理方法,再响应蓝牙状态变更代理方法;iOS8.0以下断开手机蓝牙只响应蓝牙状态变更代理方法;
蓝牙连接断开代理方法:
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
蓝牙状态变更代理方法:
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;
  1. 正常情况下需要设置一个定时器去处理连接蓝牙设备和读写交互长时间无响应的情况(如果在初始化centralManager的时候选择在主线程做蓝牙相关的操作,然后用NSTimer去处理这些无响应的情况,这样没有问题),但大多数情况下蓝牙的操作都是放在子线程去做的,为了实现多线程环境下timer的正常运行,一开始用的是NSThread+NSTimer的方式,timer正常工作了,但是timer没被释放掉,最终换成GCD的timer实现这个功能。
-(void)createConnectTimerWithInterval:(NSTimeInterval)interval timerHandler:(void (^)(void))timerHandler {
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      _connectTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
      dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC));
      dispatch_source_set_timer(_connectTimer, start, (uint64_t)(0.1 * NSEC_PER_SEC), 0);
      dispatch_source_set_event_handler(_connectTimer, ^{
        timerHandler();
      });
      dispatch_resume(_connectTimer);
}
  1. 蓝牙的操作步骤都是搜索->连接->获取services和characteristics,这三个步骤完成之后才能进行读写交互;不同的蓝牙设备携带不同services和characteristics,不同的characteristic也有不同的writeType,所以为了在一定程度上做到一劳永逸,自己写了个配置文件:

    • 配置单个service
@interface WPBluetoothService : NSObject
-(instancetype)initWithServiceUUIDString:(NSString *)serviceUUIDString sendCharacteristicUUIDString:(NSString *)sendUUIDString receiveCharacteristicUUIDString:(NSString *)receiveUUIDString writeType:(CBCharacteristicWriteType)type;
@property (nonatomic, strong) CBUUID *serviceUUID;
@property (nonatomic, strong) CBUUID *sendCharacteristicUUID;
@property (nonatomic, strong) CBUUID *receiveCharacteristicUUID;
@property (nonatomic, strong) CBService *service;
@property (nonatomic, strong) CBCharacteristic *sendCharacteristic;
@property (nonatomic, strong) CBCharacteristic *receiveCharacteristic;
@property (nonatomic, assign) CBCharacteristicWriteType writeType;
-(BOOL)isAvailable;
- 配置整个蓝牙设备携带的service
@interface WPBluetoothServiceProfile : NSObject
@property(nonatomic, strong) NSArray *bleServices;
@property(nonatomic, assign) BOOL batteryServiceEnable;
@property(nonatomic, assign) BOOL showSystemPowerAlert;
+(WPBluetoothServiceProfile *)sharedProfile;
-(WPBluetoothService *)serviceWithServiceUUID:(CBUUID *)uuid;
-(WPBluetoothService *)serviceWithSendCharacteristicUUID:(CBUUID *)uuid;
-(WPBluetoothService *)serviceWithReceiveCharacteristicUUID:(CBUUID *)uuid;
-(NSArray *)allServiceUUIDs;
-(void)resetAllServices;
-(BOOL)allServicesAvailable;

使用蓝牙功能之前,只能配置每个WPBluetoothService对象的CBUUID类型的属性,在连接成功成功后获得services和characteristics成功才能填充剩下的属性值。

-(void)configBluetoothService {
    NSMutableArray *services = [[NSMutableArray alloc] init];
    [services addObject: [[WPBluetoothService alloc] initWithServiceUUIDString:kCommandServiceId
                                                    sendCharacteristicUUIDString:kCommandSendCharacteristicUUIDString
                                                 receiveCharacteristicUUIDString:kCommandReceiveCharacteristicUUIDString
                                                                       writeType:CBCharacteristicWriteWithResponse]];
    
    [WPBluetoothServiceProfile sharedProfile].bleServices = services;
    [WPBluetoothServiceProfile sharedProfile].batteryServiceEnable = YES;
    [WPBluetoothServiceProfile sharedProfile].showSystemPowerAlert = YES;
}
发现services(仅贴核心代码,省略一些基础值的判断):
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
         [[WPBluetoothServiceProfile sharedProfile] resetAllServices];
         for (CBService *service in services) {
           WPBluetoothService *bleService = [[WPBluetoothServiceProfile sharedProfile] serviceWithServiceUUID:service.UUID];
           if (bleService) {
             bleService.service = service;
             continue;
            }
           if ([service.UUID isEqual:[CBUUID UUIDWithString:kBatteryServiceId]]) {
             batteryServiceId = service;
           }
         }
         for (WPBluetoothService *bleService in [WPBluetoothServiceProfile sharedProfile].bleServices) {
             if (bleService.service) {
                  [peripheral discoverCharacteristics: [NSArray arrayWithObjects:bleService.sendCharacteristicUUID, bleService.receiveCharacteristicUUID, nil]
                                           forService:bleService.service];
              }
          }
}
发现characteristics(仅贴核心代码,省略一些基础值的判断):
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service
error:(NSError *)error {
        WPBluetoothService *bleService = [[WPBluetoothServiceProfile sharedProfile] serviceWithServiceUUID:service.UUID];
        if(bleService) {
          for (CBCharacteristic *characteristic in characteristics) {
            if ([characteristic.UUID isEqual:bleService.receiveCharacteristicUUID]) {
                bleService.receiveCharacteristic = characteristic;
                [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            } else if ([characteristic.UUID isEqual:bleService.sendCharacteristicUUID]) {
                bleService.sendCharacteristic = characteristic;
            }
          }
        }
}

Related posts:

  • iOS:NSTimer使用小记
  • 选择GCD还是NSTimer

你可能感兴趣的:(iOS蓝牙开发小结)