Я использую Android 4.4.2 на Nexus 7. У меня есть периферийное устройство с низкой энергией bluetooth, услуги которого меняются при перезагрузке. Приложение Android использует BluetoothGatt.discoverServices(). Однако Android только один раз запрашивает периферийные устройства для обнаружения служб, последующие вызовы DiscoverServices() приводят к кэшированию данных с первого вызова, даже между отключением. Если я отключу/активирую адаптер Android bt, то DiscoverServices() обновит кеш, запросив периферию. Есть ли программный способ заставить Android обновить свой кеш-сервис без отключения/включения адаптера?
Как программно заставить обнаружение услуг с низким энергопотреблением Bluetooth на Android без использования кеша
Ответ 1
У меня была такая же проблема. Если вы видите исходный код 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;
}
Этот метод действительно очищает кеш с устройства Bluetooth. Но проблема в том, что у нас нет доступа к ней. Но в java мы имеем отражение, поэтому мы можем получить доступ к этому методу. Вот мой код для подключения Bluetooth-устройства, обновляющего кеш.
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;
}
Ответ 2
Действительно, ответ Мигеля работает. Чтобы использовать refreshDeviceCache, мне удастся выполнить этот порядок:
// Attempt GATT connection
public void connectGatt(MyBleDevice found) {
BluetoothDevice device = found.getDevice();
gatt = device.connectGatt(mActivity, false, mGattCallback);
refreshDeviceCache(gatt);
}
Это работает для ОС 4.3 до 5.0, протестированных с помощью периферийных устройств Android и iPhone.
Ответ 3
Перед сканированием устройства используйте следующее:
if(mConnectedGatt != null) mConnectedGatt.close();
Это отключит устройство и очистит кэш, и, следовательно, вы сможете подключиться к тому же устройству.
Ответ 4
В некоторых устройствах, даже если вы отключите разъем, соединение не закончится из-за кеша. Вам необходимо отключить удаленное устройство, используя класс BluetoothGatt. Как ниже
BluetoothGatt mBluetoothGatt = device.connectGatt(appContext, false, new BluetoothGattCallback() {
};);
mBluetoothGatt.disconnect();
Примечание. Эта логика работала для меня в устройствах на китайском языке.
Ответ 5
Вот версия Kotlin с RxAndroidBle для обновления:
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
}
}
Фактический звонок:
Observable.just(rxBleConnection)
.flatMap { rxBleConnection -> rxBleConnection.queue(CustomRefresh()) }
.observeOn(Schedulers.io())
.doOnComplete{
switchToDFUmode()
}
.subscribe({ isSuccess ->
// check
},
{ throwable ->
Timber.d(throwable)
}).also {
refreshDisposable.add(it)
}