GATTサーバーへの接続中にonServicesDiscoveredが呼び出されることはありません

Nexus 5X(Android 7.1を実行)とペアになっているBluetoothヘッドセットがあり、ヘッドセットの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

ご覧のとおり、 onServicesDiscovered は起動されません。 TRANSPORT_LE 参照)を指定して connectGatt を呼び出そうとしましたが、 onConnectionStateChange:133、0 が返されます。 この質問も見つけたのでを追加しました答え2で述べたようにgatt.connect()メソッド。

onServicesDiscovered コールバックが表示されない理由は何かありますか。

5
ああ、すみません、それはテストのためでした。上記のコードで変更しましたが、動作しません。
追加された 著者 Cilenco,
コード例では、 mBluetoothGatt.discoverServices(); とローカル変数 BluetoothGatt gatt = device.connectGatt(this、false、btleGattCallback); を参照します。それらが同じインスタンスまたはデバイスではない可能性がありますか?
追加された 著者 ayvazj,

4 答え

私にとって本当に便利なことは、接続が確立されてから約600ミリ秒待ってからサービス検出を開始することです。

6
追加された
うわー、うまくいった!ありがとうございます。 onServicesDiscovered が呼び出されましたが、 gatt.getServices()を呼び出すと空のリストが返されます。何かアイディアは?
追加された 著者 Cilenco,
@Cilencoあなたはそのステータス== GATT_SUCCESSをチェックすることによってサービス発見が実際に成功したことを確かめる必要もあります。詳細については、これを確認してください。 developer.android。 com/reference/android/bluetooth /&hellip; 、int)。そうでなかったら、それから多分あなたはサービス発見を始める前にもっと長い間待つ必要があります。それでもうまくいかない場合は、周辺機器に他の問題がある可能性があります。
追加された 著者 Roberto Betancourt,

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 をローカル変数ではなくフィールド変数にしてみてください。

重要な作業をしている場合は、高水準ロジックに集中できるように、すべての特異性を隠すライブラリを使用してみてください。 https://github.com/Polidea/RxAndroidBle

これが特性の読み方の例です。

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

またはJava 7の構文で

        connectionObservable
                .flatMap(new Func1>() {
                    @Override
                    public Observable call(RxBleConnection rxBleConnection) {
                        return rxBleConnection.readCharacteristic(characteristicUuid);
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber() {
                    @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));
                    }
                });
6
追加された
これも助けにはなりませんが、APIは良さそうです:)例えば電池残量の見方の例を教えてください。私は新しいラムダと例で参照しているメソッドと少し混乱しています...
追加された 著者 Cilenco,
コードをありがとう:)しかし今、私は STARTED RxBleRadioOperationConnect を受け取り、その後 onConnectionStateChange newState = 0 status = 133 そしてすべてがUIスレッドにあります...この?何らかの方法で TRANSPORT_BREDR を転送方法として設定することは可能ですか?
追加された 著者 Cilenco,
Lambdaとメソッドの参照に慣れるのに少し時間がかかりますが、それは多くの定型作業を節約します。私はJava 7構文を使った例で私の答えを更新しました。ラムダとメソッド参照は慣れるのに少し時間がかかりますが、それは多くのボイラープレートを節約します。
追加された 著者 ayvazj,
status = 133は、ネイティブ(C/C ++)BLEスタックからの一般的なエラーです。ほとんどの場合、開いているBLE接続を閉じないことが原因で、接続制限を超えたために接続を開くことができない状態になります。
追加された 著者 ayvazj,
必ずしもUIスレッドで実行する必要はなく、すべての呼び出しを同じスレッドで実行する必要があります。エグゼキュータスレッドを作成し、そこからすべてのコールバックを実行させます。魅力のように動作します:)
追加された 著者 Re'em,

これは役に立つかもしれません

2つのステップで説明します。サービスの接続と発見

connecting: connect from mainthread

自動再接続をfalseに設定します

VersionがM以上の場合は、Tranposrt typeを設定します。

それ以外の場合は、リフレクションを直接使用して適切に処理する

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

disover services : in onGhattListener , if device is connected fire discoverServices() from main thread

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 -> {

            }
        }
    }

これはあなたの問題を解決するかもしれません

リフレクション付きのrefreshメソッドを呼び出す

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
}
0
追加された

実際に、私はメソッドmBluetoothGatt.discoverServices()を数回(10回以上)実行することでこの問題を解決します、

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