简体   繁体   中英

didDiscoverCharacteristics not getting called when using CoreBluetooth from Java via JNI

I'm implementing a Java interface for Bluetooth Low Energy on MacOS. We want to call into the CoreBluetooth API via JNI. I'm able to scan for devices and populate services, but I can't get characteristics to work because didDiscoverCharacteristics does not seem to be getting called.

When running as an executable, devices can be scanned for, connected to, and their services/characteristics populated and printed in C++. The app then gets built as a dynamic library, and can be loaded in Java. In Java, the device is scanned for and connected to successfully, and its services can be discovered. But characteristics are never discovered when called from Java.

Here is the flow of the service population, with some superfluous parts left out for clarity:

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

I tried setting an array of CBServices in Macable only when Swiftable has discovered all services and characteristics. I tried implementing a "Middleman" between Macable and Swiftable, where every time Swiftable's PeripheralDelegate discovers services or characteristics for a service, it updates the Middleman's list of services. And again, everything works as an executable but not when using the .dylib in Java.

I am wondering how I can force any of the C++, Obj-C++ or Swift layers to wait until the "didDiscoverCharacteristics" function is invoked by Swift. If I use a while loop to wait for characteristics, the main thread gets blocked and my program runs forever waiting for a characteristic discovery that never happens. And if I force my program to sleep, the thread seems to be abandoned when using the .dylib in Java.

It seems to me that the logic of discovering devices and service population is the same as waiting for characteristic discovery, but only the first two are working correctly.

Based on this JS library , I know that it is possible to implement an API for CoreBluetooth in a non-native language.

UPDATE: It was an issue with Java. Our App was using Java 8 to talk to JNI. Updating to Java 9 completely solved the problem with no changes needed on the Swift side.

CoreBluetooth tends to behave this way when you fail to retain a reference to the relevant object. Make sure you retaining durable references to the peripherals, services, and characteristics you're interested in for the lifetime of the BLE conversation.

Java 8's JNI might be incompatible with the way CoreBluetooth handles threading. You can solve this issue by using JDK 9 instead of 8.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM