CoreBluetooth状态保存问题:在iOS 7.1中未调用willRestoreState

时间:2014-03-14 17:53:40

标签: ios iphone objective-c bluetooth

CoreBluetooth状态保存问题:在iOS 7.1中未调用willRestoreState

嘿所有人。过去几周我一直致力于蓝牙LE项目,并遇到了障碍。我无法在iOS 7 / 7.1中正常恢复状态。我遵循了(我认为)Apple提出的所有步骤,并在其他堆栈溢出帖子上获得了一些线索。

  1. 我为plist添加了正确的蓝牙权限
  2. 当我创建我的中央管理器时,我给它一个恢复标识符键。
  3. 我总是使用相同的密钥
  4. 来实例化CM
  5. 我将willRestoreState函数添加到CM委托
  6. 我的测试用例:

    1. 连接到外围设备
    2. 确认连接
    3. 模拟内存驱逐(kill(getpid(),SIGKILL);)
    4. 传输数据
    5. 结果iOS 7:

      应用程序将在AppDelegate didFinishLaunchingWithOptions函数中响应,但launchOptions [UIApplicationLaunchOptionsBluetoothCentralsKey]内的NSArray内容始终为空数组。

      iOS 7.1上的结果:

      进展!我可以在100%的时间内在UIApplicationLaunchOptionsBluetoothCentralsKey数组中看到我的CentralManager密钥,但永远不会调用fromRestoreState。

      代码:

      //All of this is in AppDelegate for testing
      
      @import CoreBluetooth;
      @interface AppDelegate () <CBCentralManagerDelegate, CBPeripheralDelegate>
      @property (readwrite, nonatomic, strong) CBCentralManager *centralManager;
      @end
      
      - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
      
          self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionRestoreIdentifierKey:@“myCentralManager”}];
      
          //Used to debug CM restore only
          NSArray *centralManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];
          NSString *str = [NSString stringWithFormat: @"%@ %lu", @"Manager Restores: ", (unsigned long)centralManagerIdentifiers.count];
          [self sendNotification:str];
          for(int i = 0;i<centralManagerIdentifiers.count;i++)
          {
              [self sendNotification:(NSString *)[centralManagerIdentifiers objectAtIndex:i]];
          }
      
          return YES;
      }
      
      - (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary *)state {
          activePeripheral = [state[CBCentralManagerRestoredStatePeripheralsKey] firstItem];
          activePeripheral.delegate = self;
      
          NSString *str = [NSString stringWithFormat: @"%@ %lu", @"Device: ", activePeripheral.UUID];
          [self sendNotification:str];
      }
      
      //sendNotification is a func that creates a local notification for debugging when device connected to comp
      

      当我运行测试时,当应用程序不在内存中时,当我的BLE设备与手机通信时,didFinishLaunchWithOptions被调用为100%,但永远不会调用willRestoreState。

      任何和所有帮助都会很棒!谢谢!

4 个答案:

答案 0 :(得分:14)

好的,所以我不得不删除我对这个问题的两个答案。但我想我终于明白了。

This comment是您问题的关键。本质上,只有在操作系统关闭时才会调用此centralManager:willRestoreState:,而外围设备正在执行未完成的操作(这不包括扫描外围设备进一步调查,如果你'重新扫描服务UUID并且应用程序以相同的方式被杀死,或者您已经完成连接,它实际上会调用您的代理人。)

要复制: 我在MacBook上设置了使用CoreBluetooth的外围设备。我在外围设备上做广告,让我的iPhone上的中心发现它。然后,让OSX外围设备应用程序继续运行,终止Mac上的BT连接,然后从iOS设备的中央启动连接。这显然将继续运行,因为外围设备是不可达的(显然,连接尝试可以永久持续,因为蓝牙LE在连接上没有超时)。然后我给我的gui添加了一个按钮,并将它连接到我的视图控制器中的一个函数:

- (IBAction)crash:(id)sender
{
    kill(getpid(), SIGKILL);
}

这会杀死应用程序,好像它被操作系统杀死一样。一旦您尝试连接,请点按该按钮以使应用程序崩溃(有时需要两次点击)。

在Mac上激活蓝牙将导致iOS重新启动您的应用并调用正确的处理程序(包括centralManager:willRestoreState:)。

如果要调试处理程序(通过设置断点),在Xcode中,在Mac上打开BT之前,设置断点然后选择“Debug&gt;”。附加到流程...&gt;按流程标识符或名称...'。

在显示的对话框中,键入应用程序的名称(应与目标相同),然后单击“附加”。然后Xcode将在状态窗口中等待启动。等待几秒钟然后在OSX上打开BT。确保您的外围设备仍然是广告,然后iOS会选择它并重新启动您的应用以处理连接。

可能还有其他方法可以对此进行测试(可能会使用特征上的通知?)但是此流程可100%重现,因此可能会帮助您最轻松地测试代码。

答案 1 :(得分:3)

有同样的问题。根据我的工作原理,您需要在实例化CBCentralManager时使用自定义调度队列,并且将触发willRestoreState方法。我认为这是由于当您的应用程序由后台恢复线程启动时,默认队列(当使用&#34; nil&#34;)时不会处理异步事件。

    ...
    dispatch_queue_t centralQueue = dispatch_queue_create("com.myco.cm", DISPATCH_QUEUE_SERIAL);

    cm = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue options:@{CBCentralManagerOptionRestoreIdentifierKey:@"cmRestoreID",CBCentralManagerOptionShowPowerAlertKey:@YES}];

    ...

答案 2 :(得分:-1)

您需要将CentralManager移动到自己的队列中。

答案 3 :(得分:-2)

您还需要使用还原标识符实例化外围设备。