onServicesDiscovered never called while connecting to GATT Server

前端 未结 5 346
闹比i
闹比i 2020-12-17 20:04

I have a bluetooth headset which is paired with my Nexus 5X (running Android 7.1) and I would like to connect to a GATT Server of the headset. I tried it with the following

相关标签:
5条回答
  • 2020-12-17 20:18

    Had the same problem, but waiting 600 ms wasn't enough. This is probably due to the BLE module used. I fixed the problem by calling my method

    discoverServices(); 
    

    after calling

    device.connectGatt(this,false,gattCallback)
    

    I'm basically just calling discoverServices every 5 seconds (this is arbitrarily chosen)

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

    In the onServicesDiscovered(...) method of my gattCallback I make gattConnected true. This worked for me.

    0 讨论(0)
  • 2020-12-17 20:31

    In fact,I solve this problem by Execution the method mBluetoothGatt.discoverServices() several times(10 or more),

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

    BLE on Android can be a little finicky.

    Make sure you are calling mBluetoothGatt.discoverServices() on the UI thread.

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

    Also try making BluetoothGatt gatt a field variable instead of a local variable.

    If you are doing any significant work, try using a library that masks all of the idiosyncrasies so you can focus on the high level logic. https://github.com/Polidea/RxAndroidBle.

    Here is an example of how to read a characteristic.

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

    Or with Java 7 syntax

            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));
                        }
                    });
    
    0 讨论(0)
  • 2020-12-17 20:40

    This may help

    I will explain in two steps: connecting and discovering services

    connecting: connect from mainthread

    set auto-reconnect to false

    if version greater than or equals to M, set Transport type

    else directly use reflection and handle it properly

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

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

    this may solve your problem

    call refresh method with reflection

    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 讨论(0)
  • 2020-12-17 20:42

    Something that has been really useful for me is to wait for about 600ms after the connection has been established and then start the service discovery.

    0 讨论(0)
提交回复
热议问题