如何在不使用缓存的情况下以编程方式强制在Android上强制蓝牙低能耗服务发现

时间:2014-03-23 20:55:35

标签: android bluetooth bluetooth-lowenergy service-discovery

我在Nexus 7上使用Android 4.4.2。 我有一个蓝牙低功耗外设,其服务在重新启动时会发生变化。 Android应用程序调用BluetoothGatt.discoverServices()。然而,Android只查询外围设备一次以发现服务,后续调用discoverServices()会导致第一次调用的缓存数据,甚至断开连接之间。 如果我禁用/启用Android bt适配器,则discoverServices()通过查询外设来刷新缓存。 有没有一种程序化的方式来强制Android刷新它的' ble服务缓存而不禁用/启用适配器?

5 个答案:

答案 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
  }
}