如何在不使用缓存的情况下以编程方式在 Android 上强制发现蓝牙低能耗服务
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22596951/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to programmatically force bluetooth low energy service discovery on Android without using cache
提问by monzie
I am using Android 4.4.2 on a Nexus 7. I have a bluetooth low energy peripheral whose services change when it is rebooted. The android app calls BluetoothGatt.discoverServices(). However Android only queries the peripheral once to discover services, subsequent calls to discoverServices() result in the cached data from the first call, even between disconnections. If I disable/enable the Android bt adapter then discoverServices() refreshes the cache by querying the peripheral. Is there a programmatic way to force Android to refresh its' ble services cache without disabling/enabling the adapter?
我在 Nexus 7 上使用 Android 4.4.2。我有一个蓝牙低功耗外设,它的服务在重新启动时会发生变化。android 应用调用BluetoothGatt.discoverServices()。然而,Android 只查询一次外设来发现服务,后续调用 discoverServices() 会导致第一次调用的缓存数据,即使在断开连接之间也是如此。如果我禁用/启用 Android bt 适配器,则 discoverServices() 通过查询外围设备刷新缓存。是否有一种编程方式可以强制 Android 在不禁用/启用适配器的情况下刷新其 ble 服务缓存?
回答by Miguel
I just had the same problem. If you see the source code of BluetoothGatt.javayou can see that there is a method called refresh()
我只是遇到了同样的问题。如果您查看BluetoothGatt.java的源代码,您可以看到有一个名为refresh()的方法
/**
* Clears the internal cache and forces a refresh of the services from the
* remote device.
* @hide
*/
public boolean refresh() {
if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
try {
mService.refreshDevice(mClientIf, mDevice.getAddress());
} catch (RemoteException e) {
Log.e(TAG,"",e);
return false;
}
return true;
}
This method does actually clear the cache from a bluetooth device. But the problem is that we don't have access to it. But in java we have reflection, so we can access this method. Here is my code to connect a bluetooth device refreshing the cache.
这种方法实际上会从蓝牙设备中清除缓存。但问题是我们无法访问它。但是在java中我们有反射,所以我们可以访问这个方法。这是我连接蓝牙设备刷新缓存的代码。
private boolean refreshDeviceCache(BluetoothGatt gatt){
try {
BluetoothGatt localBluetoothGatt = gatt;
Method localMethod = localBluetoothGatt.getClass().getMethod("refresh", new Class[0]);
if (localMethod != null) {
boolean bool = ((Boolean) localMethod.invoke(localBluetoothGatt, new Object[0])).booleanValue();
return bool;
}
}
catch (Exception localException) {
Log.e(TAG, "An exception occured while refreshing device");
}
return false;
}
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG,"BluetoothAdapter not initialized or unspecified address.");
return false;
}
// Previously connected device. Try to reconnect.
if (mBluetoothGatt != null) {
Log.d(TAG,"Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect()) {
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter
.getRemoteDevice(address);
if (device == null) {
Log.w(TAG, "Device not found. Unable to connect.");
return false;
}
// We want to directly connect to the device, so we are setting the
// autoConnect
// parameter to false.
mBluetoothGatt = device.connectGatt(MyApp.getContext(), false, mGattCallback));
refreshDeviceCache(mBluetoothGatt);
Log.d(TAG, "Trying to create a new connection.");
return true;
}
回答by Thomas
Indeed Miguel's answer works. To use refreshDeviceCache, I'm successful with this calling order:
事实上,米格尔的回答有效。要使用 refreshDeviceCache,我成功地使用了这个调用顺序:
// Attempt GATT connection
public void connectGatt(MyBleDevice found) {
BluetoothDevice device = found.getDevice();
gatt = device.connectGatt(mActivity, false, mGattCallback);
refreshDeviceCache(gatt);
}
This works for OS 4.3 through 5.0 tested with Android and iPhone Peripherals.
这适用于使用 Android 和 iPhone 外设测试的 OS 4.3 到 5.0。
回答by Satheesh
In Some Devices, Even you disconnect the socket the connection wont end because of the cache. You need to disconnect the remote device by using the BluetoothGatt Class. As Below
在某些设备中,即使您断开套接字连接也不会因为缓存而终止。您需要使用 BluetoothGatt 类断开远程设备。如下
BluetoothGatt mBluetoothGatt = device.connectGatt(appContext, false, new BluetoothGattCallback() {
};);
mBluetoothGatt.disconnect();
Note : This logic worked for me in china based devices
注意:这个逻辑在中国设备上对我有用
回答by Kebab Krabby
Here is the Kotlin version with RxAndroidBle for refresh:
这是带有 RxAndroidBle 的 Kotlin 版本用于刷新:
class CustomRefresh: RxBleRadioOperationCustom<Boolean> {
@Throws(Throwable::class)
override fun asObservable(bluetoothGatt: BluetoothGatt,
rxBleGattCallback: RxBleGattCallback,
scheduler: Scheduler): Observable<Boolean> {
return Observable.fromCallable<Boolean> { refreshDeviceCache(bluetoothGatt) }
.delay(500, TimeUnit.MILLISECONDS, Schedulers.computation())
.subscribeOn(scheduler)
}
private fun refreshDeviceCache(gatt: BluetoothGatt): Boolean {
var isRefreshed = false
try {
val localMethod = gatt.javaClass.getMethod("refresh")
if (localMethod != null) {
isRefreshed = (localMethod.invoke(gatt) as Boolean)
Timber.i("Gatt cache refresh successful: [%b]", isRefreshed)
}
} catch (localException: Exception) {
Timber.e("An exception occured while refreshing device" + localException.toString())
}
return isRefreshed
}
}
Actual call:
实际调用:
Observable.just(rxBleConnection)
.flatMap { rxBleConnection -> rxBleConnection.queue(CustomRefresh()) }
.observeOn(Schedulers.io())
.doOnComplete{
switchToDFUmode()
}
.subscribe({ isSuccess ->
// check
},
{ throwable ->
Timber.d(throwable)
}).also {
refreshDisposable.add(it)
}
回答by Gyapti Jain
Use the following before scanning the device:
在扫描设备之前使用以下内容:
if(mConnectedGatt != null) mConnectedGatt.close();
This will disconnect the device and clear the cache and hence you would be able to reconnect to the same device.
这将断开设备并清除缓存,因此您将能够重新连接到同一设备。