通过 JNI 从 Java 使用 CoreBluetooth 时未调用 didDiscoverCharacteristics

didDiscoverCharacteristics not getting called when using CoreBluetooth from Java via JNI

我正在 MacOS 上实现低功耗蓝牙的 Java 接口。我们想通过 JNI 调用 CoreBluetooth API。我能够扫描设备并填充服务,但我无法使特征起作用,因为 didDiscoverCharacteristics 似乎没有被调用。

当 运行 作为可执行文件时,可以扫描、连接设备,并在 C++ 中填充和打印设备 services/characteristics。然后,该应用程序被构建为一个动态库,并且可以在 Java 中加载。 Java,设备扫描连接成功,可以发现其服务。但是从 Java.

调用时从未发现特征

这里是服务人口的流动,为清楚起见,省略了一些多余的部分:

C++:BluetoothCentralNative.cpp

// tell "macable bridge" to populate services/characteristics
macableBridge.populateServices(name, UUID);
// retreive the discovered services (with characteristics populated)
vector<vector<string>> serviceData = macableBridge.getDiscoveredServices();

Objective-C++: MacableBridge.mm

void MacableBridge::populateServices(string name, string UUID) {
    NSString* nameString = [NSString stringWithCString:name.c_str() encoding:NSUTF8StringEncoding];
    NSString* uuidString = [NSString stringWithCString:UUID.c_str() encoding:NSUTF8StringEncoding];
    CBPeripheral* foundPeripheral = [macable findPeripheral:nameString deviceUUID:uuidString];
    [macable connectToDevice:foundPeripheral];
    [macable populateServices:foundPeripheral];
}

Objective-C: Macable.m

- (void) populateServices:(CBPeripheral *)device {
    [_swift populateServicesWithDevice:device];
    // sleep needs to be here to discover characteristics successfully as executable
    // but when we sleep, thread seems to die when calling this library from java
    sleep(1);
    // more on retrieval of populated services later
}

Swiftable.swift

@objc public func populateServices(device: CBPeripheral) {
    device.discoverServices(nil);
}

PeripheralDelegate.swift

// invoked when the peripheral's available services are discovered
public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
    print("\nServices discovered!\n")
    //middleman.gotServices(peripheral.services)    (more on this later)
    for service in peripheral.services! {
        peripheral.discoverCharacteristics(nil, for: service)
    }
}

// invoked when characteristics of a specified service are discovered
public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
    print("Discovered characteristics for service: \(service)")
    //middleman.gotCharacteristics(service)
}

仅当 Swiftable 发现所有服务和特征时,我才尝试在 Macable 中设置一组 CBServices。我尝试在 Macable 和 Swiftable 之间实现一个 "Middleman",其中每次 Swiftable 的 PeripheralDelegate 发现服务或服务的特征时,它都会更新中间人的服务列表。再一次,一切都可以作为可执行文件使用,但在 Java.

中使用 .dylib 时就不行了

我想知道如何强制任何 C++、Obj-C++ 或 Swift 层等待 "didDiscoverCharacteristics" 函数被 Swift 调用。如果我使用 while 循环来等待特征,主线程就会被阻塞,我的程序将永远运行,等待从未发生过的特征发现。如果我强迫我的程序休眠,那么在 Java.

中使用 .dylib 时线程似乎被放弃了

在我看来,发现设备和服务人口的逻辑与等待特征发现的逻辑相同,但只有前两个工作正常。

基于此JS library,我知道可以用非母语为 CoreBluetooth 实现 API。

更新: 这是 Java 的问题。我们的应用程序使用 Java 8 与 JNI 通信。更新到 Java 9 完全解决了问题,Swift 方面不需要任何更改。

当您未能保留对相关对象的引用时,CoreBluetooth 往往会以这种方式运行。确保在 BLE 对话的整个生命周期内保留对您感兴趣的外围设备、服务和特征的持久引用。

Java 8 的 JNI 可能与 CoreBluetooth 处理线程的方式不兼容。您可以使用 JDK 9 而不是 8 来解决此问题。