2017-01-02 90 views
5

我有一个与我的Nexus 5X(运行Android 7.1)配对的蓝牙耳机,并且我想连接到耳机的GATT服务器。我用下面的代码试了一下:onServices在连接到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是从来没有发射。我试图拨打connectGattTRANSPORT_LEref),但后来我得到一个onConnectionStateChange: 133, 0。我还发现this question这就是为什么我加入了回答二中提到的gatt.connect()方法的原因。

你有什么想法,为什么我没有得到onServicesDiscovered回调?

+0

在你的代码示例中,你引用'mBluetoothGatt.discoverServices();'和一个局部变量'BluetoothGatt gatt = device.connectGatt(this,false,btleGattCallback);'?它可能不是同一个实例或设备吗? – ayvazj

+0

对不起,这是出于测试原因。我在上面的代码中改变了它,但它也不起作用。 – Cilenco

回答

4

东西已经非常有用,我是等待大约600毫秒的连接建立后,然后启动该服务发现。

+0

哇,工作!谢谢'onServicesDiscovered'现在被调用,但如果我调用'gatt.getServices()'它会返回一个空列表。任何想法为什么? – Cilenco

+0

@Cilenco您还必须通过检查状态== GATT_SUCCESS来验证服务发现实际上是否成功。检查此更多信息:https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html#onServicesDiscovered(android.bluetooth.BluetoothGatt,int)。如果没有,那么在开始服务发现之前可能需要等待更长的时间。如果它仍然不起作用,那么也许你的外围设备有其他问题。 –

6

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

这也没有帮助,但API看起来不错:)你能举个例子说明如何读取电池电量吗?我对新的lambda表达式以及在示例中引用的方法感到困惑... – Cilenco

+0

Lambda和方法引用需要一些习惯,但它节省了大量的锅炉板,我用一个使用Java 7的示例更新了我的答案句法。 Lambda和方法参考需要一些习惯,但它节省了大量的锅炉板。 – ayvazj

+0

谢谢你的代码:)但现在我得到了'STARTED RxBleRadioOperationConnect',之后'onConnectionStateChange newState = 0 status = 133',一切都在UI线程中......对此有何想法?是否有可能将'TRANSPORT_BREDR'设置为运输方式? – Cilenco

0

这可能有助于

我将分两个步骤说明:连接和发现服务

连接: 从mainthread连接,如果版本更大

设置的自动重新连接到假

大于或等于M,设置Tranposrt类型

其他直接用反思和妥善处理

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服务:在onGhattListener,如果设备是从主线程

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

      } 
     } 
    } 

连接消防discoverServices()这可能会解决你的问题

带反射的呼叫刷新方法

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 
}