我在Nexus 7上使用Android 4.4.2。 我有一个蓝牙低功耗外设,其服务在重新启动时会发生变化。 Android应用程序调用BluetoothGatt.discoverServices()。然而,Android只查询外围设备一次以发现服务,后续调用discoverServices()会导致第一次调用的缓存数据,甚至断开连接之间。 如果我禁用/启用Android bt适配器,则discoverServices()通过查询外设来刷新缓存。 有没有一种程序化的方式来强制Android刷新它的' ble服务缓存而不禁用/启用适配器?
答案 0 :(得分:79)
我遇到了同样的问题。如果您看到 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;
}
此方法实际上清除了蓝牙设备的缓存。但问题是我们无法访问它。 但是在java中我们有reflection,所以我们可以访问这个方法。这是我的代码,用于连接刷新缓存的蓝牙设备。
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;
}
答案 1 :(得分:2)
确实,米格尔的回答是有效的。要使用refreshDeviceCache,我已成功使用此调用顺序:
// Attempt GATT connection
public void connectGatt(MyBleDevice found) {
BluetoothDevice device = found.getDevice();
gatt = device.connectGatt(mActivity, false, mGattCallback);
refreshDeviceCache(gatt);
}
这适用于使用Android和iPhone外设测试的OS 4.3到5.0。
答案 2 :(得分:2)
扫描设备前请使用以下命令:
if(mConnectedGatt != null) mConnectedGatt.close();
这将断开设备并清除缓存,因此您可以重新连接到同一设备。
答案 3 :(得分:2)
在某些设备中,即使断开连接,连接也不会因缓存而终止。您需要使用BluetoothGatt类断开远程设备。如下
BluetoothGatt mBluetoothGatt = device.connectGatt(appContext, false, new BluetoothGattCallback() {
};);
mBluetoothGatt.disconnect();
注意:这个逻辑在我的基于设备的设备中起作用
答案 4 :(得分:0)
以下是带有RxAndroidBle的Kotlin版本以刷新:
from django.core.exceptions import ValidationError
class Booking(models.Model):
date = models.DateTimeField(null=False, blank=False)
booked_at = models.DateTimeField(auto_now_add=True)
booking_last_modified = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
# Make sure there are no bookings on the same day
if Booking.objects.exclude(pk=self.pk).filter(date__date=self.date.date).exists():
raise ValidationError('There cannot be two bookings with the same date.')
super(Booking, self).save(*args, **kwargs)
实际通话:
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
}
}