Android 连接到GATT服务器时从未调用OnServicesDiscovery
我有一个蓝牙耳机,它与我的Nexus 5X(运行Android 7.1)配对,我想连接到耳机的GATT服务器。我使用以下代码进行了尝试: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) {
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);
}
}