Android Bluetooth Código de baja energía compatible con API> = 21 Y API <21
Estoy desarrollando una aplicación que tiene que conectar con un dispositivo BLE, en mi código quiero usar el nuevo Scan y ScanCallback para BLE implementado desde API 21 (Android 5), pero tengo que mantener la compatibilidad con Android 4.3 y superior.
Así que escribí el código, por ejemplo, de esta manera:
- La exploración de dispositivos Android BLE con filtro no funciona
- ¿Cuáles son los pasos para ser notificado por el dispositivo Bluetooth Low Energy (BLE)?
- BLE Android - onConnectionStateChange no se llama
- Obtención de datos de byte scanRecord diferentes para el mismo dispositivo BLE al escanear con diferentes versiones del dispositivo android
- La devolución de llamada de BLE start de Android BLE no se dispara incluso con permisos de ubicación
if (Build.VERSION.SDK_INT >= 21) { mLEScanner.startScan(filters, settings, mScanCallback); } else { btAdapter.startLeScan(leScanCallback); }
Y he definido las 2 callbacks, una para API 21 y superior y otra para API 18 a 20:
//API 21 private ScanCallback mScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { BluetoothDevice btDevice = result.getDevice(); connectToDevice(btDevice); } public void connectToDevice(BluetoothDevice device) { if (mGatt == null) { mGatt = device.connectGatt(context, false, btleGattCallback); if (Build.VERSION.SDK_INT < 21) { btAdapter.stopLeScan(leScanCallback); } else { mLEScanner.stopScan(mScanCallback); } } } }; //API 18 to 20 private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) { btAdapter.stopLeScan(leScanCallback); runOnUiThread(new Runnable() { @Override public void run() { mBluetoothGatt = device.connectGatt(context, false, btleGattCallback); } }); } };
También he añadido la anotación
@TargetApi(21)
Pero cuando lanzo la aplicación en Android 4.x se bloquea inmediatamente informando el error de que la clase ScanCallback no se puede encontrar (la que se pretende que se utiliza sólo con Android 5 y superiores).
¿Como puedo resolver esto?
Muchas gracias. Daniele.
- Android Lollipop 5.0 Bluetooth Low Energy función central de mal rendimiento
- Problemas de conectividad BLE de Marshmallow de Android 6.0
- ¿Por qué el "" BluetoothAdapter: startLeScan (): null "" ocurrió cuando BLE Scan en Android?
- ¿Cuántos dispositivos podemos emparejar vía Bluetooth de BLE a Android?
- Android: perfil de baja energía del GATT de Bluetooth
- BLE GATT onCharacteristicChanged no se llama después de suscribirse a la notificación
- Android Bluetooth Baja energía: characteristic.getPermissions () devuelve 0?
- ¿Existe alguna manera de conectarse a un dispositivo Bluetooth Low Energy bajo Android 5 exponiendo la dirección estática pública?
Después de leer varios posts hice lo siguiente. Por si acaso, aquí está la documentación de Android sobre BluetoothLe
primero
Cree dos métodos uno scanLeDevice21
y scanLeDevice18
. En scanLeDevice21
agregue la anotación @RequiresApi(21)
que dice:
Indica que el elemento anotado sólo debe ser llamado en el nivel de API dado o superior. Esto es similar en propósito a la anotación más antigua de @TargetApi, pero expresa más claramente que esto es un requisito en la persona que llama, en lugar de ser utilizado para "suprimir" las advertencias dentro del método que exceden a la minSdkVersion.
Segundo
Implementar cada método, aquí está mi código.
@RequiresApi(21) private void scanLeDevice21(final boolean enable) { ScanCallback mLeScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); BluetoothDevice bluetoothDevice = result.getDevice(); if (!bluetoothDeviceList.contains(bluetoothDevice)) { Log.d("DEVICE", bluetoothDevice.getName() + "[" + bluetoothDevice.getAddress() + "]"); bluetoothDeviceArrayAdapter.add(bluetoothDevice); bluetoothDeviceArrayAdapter.notifyDataSetChanged(); } } @Override public void onBatchScanResults(List<ScanResult> results) { super.onBatchScanResults(results); } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); } }; final BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); if (enable) { // Stops scanning after a pre-defined scan period. mHandler.postDelayed(() -> { mScanning = false; swipeRefreshLayout.setRefreshing(false); bluetoothLeScanner.stopScan(mLeScanCallback); }, SCAN_PERIOD); mScanning = true; bluetoothLeScanner.startScan(mLeScanCallback); } else { mScanning = false; bluetoothLeScanner.stopScan(mLeScanCallback); } } /** * Scan BLE devices on Android API 18 to 20 * * @param enable Enable scan */ private void scanLeDevice18(boolean enable) { BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice bluetoothDevice, int rssi, byte[] scanRecord) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { bluetoothDeviceArrayAdapter.add(bluetoothDevice); bluetoothDeviceArrayAdapter.notifyDataSetChanged(); } }); } }; if (enable) { // Stops scanning after a pre-defined scan period. mHandler.postDelayed(() -> { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); }, SCAN_PERIOD); mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback); } else { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } }
Tercero
Cada vez que necesite escanear dispositivos rodea su código preguntando qué versión es. Por ejemplo, tengo un RefreshLayout
para mostrar la lista de dispositivos. Aquí está el resultado:
/** * Refresh listener */ private void refreshScan() { if (!hasFineLocationPermissions()) { swipeRefreshLayout.setRefreshing(false); //Up to marshmallow you need location permissions to scan bluetooth devices, this method is not here since is up to you to implement it and it is out of scope of this question. requestFineLocationPermission(); } else { swipeRefreshLayout.setRefreshing(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { scanLeDevice21(true); } else { scanLeDevice18(true); } } }
Y eso es.
Olvídate de la extensión, las clases de subclase que realmente no necesita como respuesta ulusoyca.
- Cree la clase
AbstractBluetoothLe
y la interfazIBleScanCallback
.IBleScanCallback
interfazIBleScanCallback
es una interfaz de marcadores. En otro dicho, una interfaz sin métodos. También puede agregar métodos a la interfaz si es necesario. Estos métodos harán la misma funcionalidad para todo tipo de scanCallbacks, es decir, getListOfFoundBleDevices (), clearListOfFoundBleDevices () etc. - Crear
BluetootLeLollipop
, yBluetoothLeJellyBean
clases que ampliar la claseAbstractBluetoothLe
. Crea tambiénBluetootLeMarshmallow
claseBluetootLeMarshmallow
que extiendeBluetootLeLollipop
claseBluetootLeLollipop
.AbstractBluetoothLe
claseAbstractBluetoothLe
tiene el campo protegidomIBleScanCallback
que es un objetoIBleScanCallback
. - Crear la clase
BleScanCallbackBase
que implementaIBleScanCallback
. - Cree la clase
LollipopScanCallback
que extiende la claseScanCallback
e implementa la interfazIBleScanCallback
. . Esta clase tiene un campo protegidoscanCallback
que será instanciado comoBleScanCallbackBase
objeto. Cree también la claseMarshmallowScanCallback
que extiende la claseLollipopScanCallback
. - Cree una clase
JellyBeanScanCallback
que extiendaBleScanCallbackBase
e implementeBluetoothAdapter.LeScanCallback
- En
BleScanCallbackBase
anular el método:onScanCallback(...)
- En
LollipoScanCallback
anulaonScanResult(int callbackType, ScanResult result)
y dentro de este método llama al métodoonScanCallback(...)
del objetoscanCallback
. - En
JellyBeanScanCallback
anulaonLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord)
y dentro de este método llame aonScanCallback(...)
- Por último, haga lo que sea necesario cuando se encuentra un dispositivo en el
onScanCallback(...)
de la claseBleScanCallbackBase
.
En resumen, lea acerca de la composición sobre la herencia, sé que esto no es una respuesta a su pregunta, pero esta es una manera ordenada de lo que desea lograr al final. Aquí está el diagrama de la clase:
Su código se está bloqueando porque está creando una clase interna anónima. Por lo tanto, en tiempo de ejecución no encuentra que sCanCallback clase.
Intente abajo y comparta el resultado. Antes de intentar esto, asegúrese de comentar la devolución de llamada (ScanCallback).
if (Build.VERSION.SDK_INT >= 21) { mLEScanner.startScan(filters, settings, new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { BluetoothDevice btDevice = result.getDevice(); connectToDevice(btDevice); } public void connectToDevice(BluetoothDevice device) { if (mGatt == null) { mGatt = device.connectGatt(context, false, btleGattCallback); if (Build.VERSION.SDK_INT < 21) { btAdapter.stopLeScan(leScanCallback); } else { mLEScanner.stopScan(mScanCallback); } } } }; } else { btAdapter.startLeScan(leScanCallback); }
- animateLayoutChanges no animar la eliminación de la flecha hacia atrás desde la barra de herramientas
- Cómo mostrar el estado de la conexión bluetooth en android