web-dev-qa-db-ja.com

キャッシュを使用せずにBluetooth低エネルギーサービスの検出をプログラムで強制的にAndroid

Nexus 7でAndroid 4.4.2を使用しています。再起動するとサービスが変わるBluetooth低エネルギー周辺機器があります。AndroidアプリはBluetoothGattを呼び出します.discoverServices()。ただし、Androidは周辺機器に1回だけクエリしてサービスを検出し、discoverServices()への後続の呼び出しは、切断間であっても最初の呼び出しからキャッシュされたデータになります。 Android bt adapter then discoverServices()は、周辺機器にクエリを実行してキャッシュを更新します。プログラムの方法で、強制的にAndroidアダプターを無効/有効にしますか?

39
monzie

私はちょうど同じ問題を抱えていました。 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では reflection があるため、このメソッドにアクセスできます。キャッシュを更新する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;
    }
80
Miguel

デバイスをスキャンする前に、次を使用してください。

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

これにより、デバイスが切断され、キャッシュがクリアされるため、同じデバイスに再接続できます。

2
Gyapti Jain

一部のデバイスでは、ソケットを切断しても、キャッシュが原因で接続が終了しません。 BluetoothGattクラスを使用して、リモートデバイスを切断する必要があります。以下のように

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

注:このロジックは、中国ベースのデバイスで私のために働いた

2
Satheesh

確かにミゲルの答えは機能します。 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
Thomas

更新用の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
  }
}

実際の呼び出し:

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