things - Código de Android Low Energy Bluetooth compatible con API>=21 AND API<21
bluetooth le gatt (3)
Estoy desarrollando una aplicación que debe conectarse con un dispositivo BLE, en mi código deseo 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:
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner.startScan(filters, settings, mScanCallback);
} else {
btAdapter.startLeScan(leScanCallback);
}
Y he definido las 2 devoluciones de llamada, una para API 21 y superior, y otra para API de 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 agregué la anotación
@TargetApi(21)
pero cuando ejecuto la aplicación en Android 4.x se bloquea inmediatamente y se informa del error de que no se puede encontrar la clase ScanCallback (la que se pretende usar solo con Android 5 y versiones posteriores).
¿Como puedo resolver esto?
Muchas gracias. Daniele.
Tu código se bloquea porque está creando una clase interna anónima. Por lo tanto, en tiempo de ejecución no encuentra esa clase sCanCallback.
Pruebe a continuación 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);
}
- Cree la clase
AbstractBluetoothLe
y la interfazIBleScanCallback
.IBleScanCallback
interfazIBleScanCallback
es una interfaz de marcador. En otro refrán, una interfaz sin métodos. También puede agregar métodos a la interfaz si lo necesita. Estos métodos harán la misma funcionalidad para todo tipo de scanCallbacks, es decir, getListOfFoundBleDevices (), clearListOfFoundBleDevices (), etc. - Cree las clases
BluetoothLeJellyBean
yBluetoothLeJellyBean
que extienden la claseAbstractBluetoothLe
. Cree tambiénBluetootLeMarshmallow
claseBluetootLeMarshmallow
que amplíaBluetootLeLollipop
claseBluetootLeLollipop
.AbstractBluetoothLe
claseAbstractBluetoothLe
tiene el campo protegidomIBleScanCallback
que es un objetoIBleScanCallback
. - Cree la clase
BleScanCallbackBase
que implementaIBleScanCallback
. - Cree la clase
LollipopScanCallback
queScanCallback
claseScanCallback
e implemente la interfazIBleScanCallback
. .Esta clase tiene un campo protegidoscanCallback
que se instanciará como objetoBleScanCallbackBase
. Crear también la claseMarshmallowScanCallback
que amplía la claseLollipopScanCallback
. - Cree la clase
JellyBeanScanCallback
que amplíeBleScanCallbackBase
e implementeBluetoothAdapter.LeScanCallback
- En
BleScanCallbackBase
anula el método:onScanCallback(...)
- En
LollipoScanCallback
anuleonScanResult(int callbackType, ScanResult result)
y dentro de este método llame al métodoonScanCallback(...)
del objetoscanCallback
. - En
JellyBeanScanCallback
anulaonLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord)
y dentro de este método llamaonScanCallback(...)
- Finalmente, haga lo que sea necesario cuando se encuentre un dispositivo en el
onScanCallback(...)
de la claseBleScanCallbackBase
.
En resumen, lea acerca de la composición sobre la herencia: sé que esta no es una respuesta a su pregunta, pero esta es una forma clara de lo que quiere lograr al final. Aquí está el diagrama de clases:
Después de leer varios mensajes, 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 solo debe invocarse en el nivel API dado o superior. Esto es similar en propósito a la anterior anotación @TargetApi, pero expresa más claramente que este es un requisito para la persona que llama, en lugar de ser utilizado para "suprimir" las advertencias dentro del método que exceden la versión minSdkVersion.
Segundo
Implementa 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, rodeará su código y le preguntará qué versión es usted. Por ejemplo, tengo un RefreshLayout
para mostrar la lista de dispositivos. Este es 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 extender clases de subclases que realmente no necesitas como respuesta ulusoyca.