it-swarm.com.de

Wie programmgesteuert die Ermittlung des Bluetooth-Energiesparmodus für Android erzwungen werden kann, ohne Cache zu verwenden

Ich verwende Android 4.4.2 auf einem Nexus 7. Ich besitze ein Bluetooth-Peripheriegerät, dessen Dienste sich beim Neustart ändern .. Die Android-App ruft BluetoothGatt.discoverServices () auf. Allerdings fragt Android das Peripheriegerät nur einmal ab, um Dienste zu ermitteln. Nachfolgende Aufrufe von discoverServices () führen dazu, dass die zwischengespeicherten Daten auch beim Verbindungsabbruch zwischengespeichert werden .. Wenn ich den Android-Adapter bt/aktiviere, wird der Cache durch discoverServices () aktualisiert Gibt es eine programmatische Methode, um Android zu zwingen, seinen Cache für Services zu aktualisieren, ohne den Adapter zu deaktivieren/aktivieren?

36
monzie

Ich hatte gerade das gleiche Problem. Wenn Sie den Quellcode von BluetoothGatt.Java sehen, können Sie feststellen, dass es eine Methode namens refresh () gibt.

/**
* 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;
}

Diese Methode löscht tatsächlich den Cache von einem Bluetooth-Gerät. Aber das Problem ist, dass wir keinen Zugang dazu haben. Aber in Java haben wir Reflektion , so dass wir auf diese Methode zugreifen können. Hier ist mein Code zum Anschließen eines Bluetooth-Geräts, das den Cache aktualisiert.

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;
    }
78
Miguel

Verwenden Sie vor dem Scannen des Geräts Folgendes:

if(mConnectedGatt != null) mConnectedGatt.close();

Dadurch wird das Gerät getrennt und der Cache geleert, sodass Sie die Verbindung zu demselben Gerät wiederherstellen können.

2
Gyapti Jain

Bei einigen Geräten wird die Verbindung aufgrund des Cache nicht unterbrochen. Sie müssen das entfernte Gerät mithilfe der BluetoothGatt-Klasse trennen. Wie nachstehend

BluetoothGatt mBluetoothGatt = device.connectGatt(appContext, false, new BluetoothGattCallback() {
        };);
mBluetoothGatt.disconnect();

Hinweis: Diese Logik funktionierte für mich in China-basierten Geräten

2
Satheesh

In der Tat funktioniert die Antwort von Miguel. Um refreshDeviceCache zu verwenden, bin ich mit dieser Aufrufreihenfolge erfolgreich:

// Attempt GATT connection
public void connectGatt(MyBleDevice found) {
    BluetoothDevice device = found.getDevice();
    gatt = device.connectGatt(mActivity, false, mGattCallback);
    refreshDeviceCache(gatt);
}

Dies funktioniert für OS 4.3 bis 5.0 mit Android- und iPhone-Peripheriegeräten.

2
Thomas

Hier ist die Kotlin-Version mit RxAndroidBle zum Aktualisieren:

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

Tatsächlicher Anruf:

Observable.just(rxBleConnection)
    .flatMap { rxBleConnection -> rxBleConnection.queue(CustomRefresh()) }
    .observeOn(Schedulers.io())
    .doOnComplete{
        switchToDFUmode()
    }
    .subscribe({ isSuccess ->
      // check 
    },
    { throwable ->
        Timber.d(throwable)
    }).also {
        refreshDisposable.add(it)
    }
0
Kebab Krabby