iOS 唤醒被Kill的APP保证蓝牙的长连接


iOS基础蓝牙连接和数据通信请在CSDN搜索其他博文,这里不再赘述。

iOS保证后台蓝牙长连接的方法,经亲测过归纳为以下几项:

1.后台静音音频的循环播放

后台循环播放一个静音的音频文件来保证APP的持续存活,不失为一个曲线方法。缺点是需要后台音频播放的说明及权限,在APP审核过程中会有被拒绝的风险。APP后台持续活动,不论是一直扫描还是连接 Peripheral都可以做到蓝牙重连。

2.连接特定的 Peripheral

 iOS蓝牙连接最基本的方法是扫描,处于后台的APP无法持续的扫描蓝牙,即使使用

[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"begin  bgend=============");
        // 如果在系统规定时间内任务还没有完成,在时间到之前会调用到这个方法,一般是10分钟
    }];

来申请延长后台驻留时间也无法一直持续扫描下去。连接特定的 Peripheral保持长连接的方法就是在APP前台扫描连接过程中保存下Peripheral对象,在进入后台断开连接的情况下直接连接这个Peripheral:

- (void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary<NSString *, id> *)options;

来保持后台的蓝牙重连。

这里还有一种情况就是蓝牙设备与iOS系统配对,并且连接的情况,APP断开蓝牙连接的重连问题。这种情况下需要结合

- (NSArray<CBPeripheral *> *)retrieveConnectedPeripheralsWithServices:(NSArray<CBUUID *> *)serviceUUIDs NS_AVAILABLE(10_9, 7_0);

来获取已经配对的系统蓝牙列表,综合以上情况保持特定的Peripheral连接。

3.iBeacon唤醒APP 

为什么要做iBeacon唤醒APP呢,之前两种情况的蓝牙后台重连和长连接的情形是APP必需是存活的状态,但如果用户玩游戏、看视频之类的操作导致系统因内存原因杀掉后台的部分APP,之前的方法就没用了。iBeacon唤醒APP就能起作用了,iBeacon原理可以参考其他文章,而我们应该做的就是在进入或者离开iBeacon范围内短暂地唤醒APP,这个时间大概在10秒左右,足够进行蓝牙的重连操作了。iBeacon需要始终定位权限,以下为iBeacon唤醒的代码实现: 

- (void)initManager{
    if ([CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] && !self.iBeaconManager) {
        self.iBeaconManager = [[CLLocationManager alloc]init];
        self.iBeaconManager.delegate = self;
        [self.iBeaconManager requestAlwaysAuthorization];
        if ([self.iBeaconManager respondsToSelector:@selector(allowsBackgroundLocationUpdates)]) {
            [self.iBeaconManager setAllowsBackgroundLocationUpdates:YES];
        }
        _ibeacon = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc]initWithUUIDString:iBeaconMonitorSignal] identifier:@"iBeacon"];
        _ibeacon.notifyEntryStateOnDisplay = YES;
        [self.iBeaconManager startMonitoringForRegion:_ibeacon];
        DDLogError(@"%@ GrootiBeaconManager ibeaconManager startMonitoringForRegion",APP_TAG);
    }
}
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region{
    DDLogError(@"%@ GrootiBeaconManager ibeaconManager didStartMonitoringForRegion",APP_TAG);
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
    DDLogError(@"%@ GrootiBeaconManager ibeaconManager didEnterRegion",APP_TAG);
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
    DDLogError(@"%@ GrootiBeaconManager ibeaconManager didExitRegion",APP_TAG);
    [self.iBeaconManager stopRangingBeaconsInRegion:(CLBeaconRegion *)region];
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region{
    DDLogError(@"%@ GrootiBeaconManager ibeaconManager didRangeBeacons %@",region,APP_TAG);
    if ([[region.proximityUUID UUIDString]isEqualToString:iBeaconMonitorSignal]){
        [self.iBeaconManager stopMonitoringForRegion:region];
        [self.iBeaconManager stopRangingBeaconsInRegion:region];
        DDLogError(@"%@ GrootiBeaconManager ibeaconManager didRangeBeacons",APP_TAG);
        if (![[GrootMainViewInstance sharedInstance].mainController.currentDevice isConnected]) {
            [[GrootMainViewInstance sharedInstance].mainController loginToDevice];
            DDLogError(@"%@ GrootiBeaconManager ibeaconManager didRangeBeacons*********loginToDevice",APP_TAG);
        }
    }
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region{
    DDLogError(@"%@ GrootiBeaconManager ibeaconManager didDetermineState %ld",APP_TAG,(long)state);
    if (![[GrootMainViewInstance sharedInstance].mainController.currentDevice isConnected]) {
        [[GrootMainViewInstance sharedInstance].mainController loginToDevice];
        DDLogError(@"%@ GrootiBeaconManager ibeaconManager didDetermineState*********loginToDevice",APP_TAG);
    }
}
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error {
    DDLogError(@"ibeaconManager monitoringDidFailForRegion:%@", error);
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    DDLogError(@"ibeaconManager Location manager failed:%@", error);
}

其中iBeaconMonitorSignal 就是设备发出的iBeacon信号,需要和设备商定,类似于

#define iBeaconMonitorSignal @"10F86430-1346-11E4-9191-0800200C9A66"

设备发iBeacon信号逻辑可以与设备商定,断开连接多久之后发出iBeacon信号,与蓝牙的广播信号间隔发出等等。以上代码在进入和离开iBeacon区域均尝试了重连逻辑,经测试能完美唤醒被杀的APP,然后重连上蓝牙,保证APP与蓝牙的长连接。

didDetermineState方法会在被iBeacon唤醒时候调用,具体操作和业务需求可在此方法进行处理。

附代码下载:iBeacon代码

4.心跳包的维持 

iOS的特性就是退到后台,如果没有命令交互,APP会被睡眠一样的挂起,蓝牙连接和通信将会断开。为了维持后台长连接,方法之一就是增加心跳包逻辑。每隔20s或者10s、30s蓝牙设备主动发起一个交互到App,作为维持连接的一种方式。心跳包增加以后,App即使退到后台,因为每20s有一个命令交互,后台活动将不会被睡眠,维持了设备和App的长连接。 

评论 24 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:创作都市 设计师:CSDN官方博客 返回首页

打赏作者

山茶Asten

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值