Android 连接到GATT服务器时从未调用OnServicesDiscovery

Android 连接到GATT服务器时从未调用OnServicesDiscovery,android,bluetooth,bluetooth-lowenergy,Android,Bluetooth,Bluetooth Lowenergy,我有一个蓝牙耳机,它与我的Nexus 5X(运行Android 7.1)配对,我想连接到耳机的GATT服务器。我使用以下代码进行了尝试: private BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {

我有一个蓝牙耳机,它与我的Nexus 5X(运行Android 7.1)配对,我想连接到耳机的GATT服务器。我使用以下代码进行了尝试:

private BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        Log.d(TAG, "onConnectionStateChange: " + status + ", " + newState);

        if(newState == STATE_CONNECTED) {
            Log.d(TAG, "Device connected");
            boolean ans = gatt.discoverServices();
            Log.d(TAG, "Discover Services started: " + ans);
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.d(TAG, "Number of Services: " + gatt.getServices().size());
    }
};

public void onDeviceClicked(BluetoothDevice device) {
    BluetoothGatt gatt = device.connectGatt(this, false, btleGattCallback);
    Log.d(TAG, "Connected to GATT: " + gatt.connect());
}
如果我在我的UI中单击耳机,则会调用onDeviceClicked,并显示此日志输出:

<!-- language: lang-none -->
Connected to GATT: true
onConnectionStateChange: 0, 2    // GATT_SUCCESS, STATE_CONNECTED
Device connected
Discover Services started: true

与关贸总协定有关:正确
onConnectionStateChange:0,2//GATT\u成功,状态\u已连接
设备连接
发现服务已启动:true
正如您所看到的那样,从未触发“发现的服务”。我试图用
TRANSPORT\u LE
()调用
connectGatt
,但随后我得到一个
onConnectionStateChange:133,0
。我还发现了这就是为什么我添加了答案2中提到的
gatt.connect()
方法


你知道为什么我没有得到
onServicesDiscovered
回调吗?

Android上的BLE可能有点挑剔

确保您正在UI线程上调用mBluetoothGatt.discoverServices()

if(newState == STATE_CONNECTED) {
    Log.d(TAG, "Device connected");
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            boolean ans = mBluetoothGatt.discoverServices();
            Log.d(TAG, "Discover Services started: " + ans);
        }
    });
}
还可以尝试将
BluetoothGatt-gatt
设置为字段变量,而不是局部变量

如果您正在做任何重要的工作,请尝试使用一个隐藏所有特性的库,这样您就可以专注于高级逻辑

下面是一个如何读取特征的示例

        connectionObservable
                .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUuid))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(bytes -> {
                    readOutputView.setText(new String(bytes));
                    readHexOutputView.setText(HexString.bytesToHex(bytes));
                    writeInput.setText(HexString.bytesToHex(bytes));
                }, this::onReadFailure);
或者使用Java7语法

        connectionObservable
                .flatMap(new Func1<RxBleConnection, Observable<byte[]>>() {
                    @Override
                    public Observable<byte[]> call(RxBleConnection rxBleConnection) {
                        return rxBleConnection.readCharacteristic(characteristicUuid);
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<byte[]>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        onReadFailure(e);
                    }

                    @Override
                    public void onNext(byte[] bytes) {
                        readOutputView.setText(new String(bytes));
                        readHexOutputView.setText(HexString.bytesToHex(bytes));
                        writeInput.setText(HexString.bytesToHex(bytes));
                    }
                });
可观察的连接
.flatMap(新函数1(){
@凌驾
公共可观察呼叫(RxBleConnection RxBleConnection){
返回RxBlConnection.readCharacteristic(characteristicUuid);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(新订户(){
@凌驾
未完成的公共无效(){
}
@凌驾
公共无效申报人(可丢弃的e){
再读取失败(e);
}
@凌驾
public void onNext(字节[]字节){
setText(新字符串(字节));
readHexOutputView.setText(HexString.bytesToHex(字节));
writeInput.setText(hextString.bytesToHex(字节));
}
});

对我来说非常有用的一点是,在建立连接后等待大约600毫秒,然后启动服务发现。

这可能会有所帮助

我将分两步解释:连接和发现服务

连接: 从主线程连接

将自动重新连接设置为false

如果版本大于或等于M,则设置传输类型

否则直接使用反射并正确处理

Handler(applicationContext.mainLooper).post {
                    Log.d(TAG, " Post is called inside mainlooper")
                    mBluetoothGatt = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        Log.d(TAG, " Is Or Greater than M $mBluetoothDevice")
                        mBluetoothDevice!!.connectGatt(this, false,
                                onGhattListener, TRANSPORT_LE)
                    } else {
                        Log.d(TAG, " Less than M")
                        try {
                            Log.d(TAG, " Trying TRANPORT LE with reflection")
                            val m = mBluetoothDevice!!.javaClass.getDeclaredMethod("connectGatt", Context::class.java, Boolean::class.javaPrimitiveType, BluetoothGattCallback::class.java, Int::class.javaPrimitiveType)
                            m.isAccessible = true
                            val transport = mBluetoothDevice!!.javaClass.getDeclaredField("TRANSPORT_LE").getInt(null)
                            m.invoke(mBluetoothDevice, this, false, onGhattListener, transport) as BluetoothGatt
                        } catch (e: Exception) {
                            e.printStackTrace()
                            Log.d(TAG, " Catch to call normal connection")
                            mBluetoothDevice!!.connectGatt(this, false,
                                    onGhattListener)
                        }
                    }
                    Log.d(TAG, "mBluetooth gatt is $mBluetoothGatt")
                    mBluetoothGatt?.let {
                        refreshDeviceCache(mBluetoothGatt!!)
                    }
                }
发现服务:在onGhattListener中,如果设备已连接,则从主线程启动
discoverServices()

override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int,
                                         newState: Int) {
        Log.d(TAG, "onConnectionStateChange $gatt and status $status and newstate $newState")
        when (newState) {
            BluetoothGatt.STATE_CONNECTED -> {
                Handler(Looper.getMainLooper()).post {
                    gatt.discoverServices()
                }
            }
            BluetoothGatt.STATE_DISCONNECTED -> {

            }
            BluetoothGatt.STATE_CONNECTING -> {

            }
            BluetoothGatt.STATE_DISCONNECTING -> {

            }
        }
    }
这可能会解决你的问题

使用反射调用刷新方法

fun refreshDeviceCache(gatt: BluetoothGatt): Boolean {
    try {
        val localMethod = gatt.javaClass.getMethod("refresh")
        if (localMethod != null) {
            return localMethod.invoke(gatt) as Boolean
        }
    } catch (localException: Exception) {
        Log.e(TAG, "An exception occured while refreshing device")
        localException.printStackTrace()
    }
    return false
}

事实上,我通过多次(10次或更多次)执行mbluetothgatt.discoverServices()方法来解决这个问题

    int i = 10;
    while (i > 0)
    {
        if (!mIsBLE_Finded)  //如果服务发现失败,继续执行discoverServices方法
        {
            i--;
            mBluetoothGatt.discoverServices();
            System.out.println("BLEService-->" + "尝试次数:" + i);
        }
        else //在10次的尝试中,存在某次服务发现成功了
        {
            i = -1;
        }
    }

有同样的问题,但等待600毫秒是不够的。这可能是由于使用了BLE模块。我通过调用我的方法解决了这个问题

discoverServices(); 
打完电话

device.connectGatt(this,false,gattCallback)
我基本上只是每5秒调用一次discoverServices(这是任意选择的)

在我的gattCallback的onServicesDiscovered(…)方法中,我将gattConnected设置为true。
这对我很有效。

我也面临同样的问题。 在
onStart()
方法中开始扫描。它对我有用


早些时候,我在
onCreate
方法上开始扫描。这就是为什么它对我不起作用的原因

在您的代码示例中,您引用了
mBluetoothGatt.discoverServices()
和一个局部变量
BluetoothGatt gatt=device.connectGatt(this,false,btlegatcallback)?它们可能不是同一个实例或设备吗?哦,很抱歉,这是出于测试原因。我在上面的代码中修改了它,但它也不起作用。哇,真管用!谢谢
onServicesDiscovered
现在被调用,但是如果我调用
gatt.getServices()
它将返回一个空列表。知道为什么吗?@Cilenco您还必须通过检查status==GATT_SUCCESS来验证服务发现是否确实成功。检查此项以了解更多信息:,int)。如果没有,那么您可能需要等待更长的时间才能开始服务发现。如果它仍然不能工作,那么你的外设可能还有其他问题。这也没有帮助,但是API看起来很好:)你能给我一个如何读取例如电池电量的例子吗?我对示例中新的Lambda和方法引用有点困惑…Lambda和方法引用需要一点时间来适应,但它节省了大量的时间,我用一个使用Java 7语法的示例更新了我的答案。Lambda和方法引用需要一点时间来适应,但它节省了大量的锅炉板。谢谢你的代码:)但是现在我开始运行RxBreadioOperationConnect
,在那之后,
onConnectionStateChange newState=0 status=133
,一切都在UI线程上。。。有什么想法吗?是否可以以某种方式将传输设置为传输方法?status=133是本机(C/C++)可编程堆栈的一般错误。这通常是由于未关闭打开的BLE连接导致的,这会使您处于无法再打开连接的状态,因为您已经超出了连接限制。您不一定需要在UI线程上运行它,只需要所有调用都在同一线程上即可。我有一个执行线程,让所有回调\etc从它运行。工作起来很有魅力:)谢谢你的建议,将
自动连接设置为
false
-这就解决了我的问题!阅读以上所有答案,你可以
private void discoverServices() {
    if(!gattConnected) { //just a boolean
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                gatt.discoverServices();
            }
        });
        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
            @Override
            public void run() {
                discoverServices();
            }
        }, 5000);
    }
}