Im letzten Jahr habe ich Bluetooth Low Energy ( BLE ) -Anwendungen für iOS entwickelt und es hat sich als recht einfach herausgestellt. Dann wurden sie auf Android portiert ... wie schwierig könnte das sein?
Ich kann mit Sicherheit sagen - es war schwieriger als ich es mir vorgestellt hatte, ich musste mich viel Mühe geben, um unter Android stabil zu arbeiten. Ich habe viele gemeinfreie Artikel studiert, einige erwiesen sich als fehlerhaft, viele waren sehr nützlich und halfen in dieser Angelegenheit. In dieser Artikelserie möchte ich meine Ergebnisse beschreiben, damit Sie nicht so viel Zeit mit Suchen verschwenden wie ich.
Funktionen von BLE für Android
Die Google-Dokumentation zu BLE ist sehr allgemein gehalten . In einigen Fällen fehlen wichtige Informationen oder sind veraltet. Beispielanwendungen zeigen nicht, wie BLE richtig verwendet wird. Ich habe nur wenige Quellen gefunden, wie man BLE richtig macht. Die Präsentation von Stuart Kent bietet ein hervorragendes Ausgangsmaterial. Für einige fortgeschrittene Themen gibt es einen guten nordischen Artikel .
Die Android BLE-API ist eine Operation auf niedriger Ebene . In realen Anwendungen müssen Sie mehrere Abstraktionsebenen verwenden (wie dies beispielsweise in iOS-CoreBluetooth standardmäßig erfolgt). Normalerweise müssen Sie dies selbst tun: Befehlswarteschlange, Bonding, Verbindungswartung, Fehler- und Fehlerbehandlung, Multithread-Zugriff. Die bekanntesten Bibliotheken sind SweetBlue , RxAndroidBle und Nordic . Meiner Meinung nach ist Nordisch am einfachsten zu erlernen, siehe Details hier .
Android BLE . . , ! , Samsung Google!
Android ( ) , 4,5 6. , , 133. .
, , «» . .
. BluetoothLeScanner
:
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
BluetoothLeScanner scanner = adapter.getBluetoothLeScanner();
if (scanner != null) {
scanner.startScan(filters, scanSettings, scanCallback);
Log.d(TAG, "scan started");
} else {
Log.e(TAG, "could not get scanner object");
}
filters
scanSettings
, scanCallback
:
private final ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice device = result.getDevice();
// ...do whatever you want with this found device
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
// Ignore for now
}
@Override
public void onScanFailed(int errorCode) {
// Ignore for now
}
};
ScanResult
, BluetoothDevice
, . , , ScanResult
:
Advertisement data - , UUID ,
filters
UUID .
RSSI - ( ).
… , .
ScanResult
.
Activity
, onScanResult
, Activity
, onScanResult
.
null , , UUID .
UUID
, UUID: 1810. Advertisement data UUID , . , , Advertisement data , .
. : , UUID Advertisement data, .
:
UUID BLP_SERVICE_UUID = UUID.fromString("00001810-0000-1000-8000-00805f9b34fb");
UUID[] serviceUUIDs = new UUID[]{BLP_SERVICE_UUID};
List<ScanFilter> filters = null;
if(serviceUUIDs != null) {
filters = new ArrayList<>();
for (UUID serviceUUID : serviceUUIDs) {
ScanFilter filter = new ScanFilter.Builder()
.setServiceUuid(new ParcelUuid(serviceUUID))
.build();
filters.add(filter);
}
}
scanner.startScan(filters, scanSettings, scanCallback);
UUID ( 1810
), 16-bit UUID
128-bit UUID
( 00001810-000000-1000-8000-000-00805f9b34fb
). UUID BASE_PART UUID, . .
, :
, , Polar H7 «Polar H7 391BBB014», - «Polar H7» , «391BBB014» - . . «Polar H7», ,
ScanResult
. :
String[] names = new String[]{"Polar H7 391BB014"};
List<ScanFilter> filters = null;
if(names != null) {
filters = new ArrayList<>();
for (String name : names) {
ScanFilter filter = new ScanFilter.Builder()
.setDeviceName(name)
.build();
filters.add(filter);
}
}
scanner.startScan(filters, scanSettings, scanCallback);
MAC-.
. MAC- , , , . , , Bluetooth.
String[] peripheralAddresses = new String[]{"01:0A:5C:7D:D0:1A"};
// Build filters list
List<ScanFilter> filters = null;
if (peripheralAddresses != null) {
filters = new ArrayList<>();
for (String address : peripheralAddresses) {
ScanFilter filter = new ScanFilter.Builder()
.setDeviceAddress(address)
.build();
filters.add(filter);
}
}
scanner.startScan(filters, scanSettings, scanByServiceUUIDCallback);
, UUID, MAC- . , . .
ScanSettings
ScanSettings
Android . , , :
ScanSettings scanSettings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
.setReportDelay(0L)
.build();
ScanMode
, . Bluetooth . , . 4 , Nordics :
SCAN_MODE_LOW_POWER
. Android 0.5, 4.5. , advertisement .
SCAN_MODE_BALANCED
. : 2, : 3, «» .
SCAN_MODE_LOW_LATENCY
. , Android , , . . .
SCAN_MODE_OPPORTUNISTIC
. , ! , , . Android , (. « »).
Callback Type
callback ScanResult
, 3 :
CALLBACK_TYPE_ALL_MATCHES
. Callback , advertisement . - 200-500 allback, advertisement .
CALLBACK_TYPE_FIRST_MATCH
. Callback , advertisement .
CALLBACK_TYPE_MATCH_LOST
. Callback , advertisement advertisement . .
CALLBACK_TYPE_ALL_MATCHES
CALLBACK_TYPE_FIRST_MATCH
. . - CALLBACK_TYPE_ALL_MATCHES
, callback, - CALLBACK_TYPE_FIRST_MATCH
.
Match mode
, Android «».
MATCH_MODE_AGGRESSIVE
. advertisement .
MATCH_MODE_STICKY
. , advertisement .
, MATCH_MODE_AGGRESSIVE
, .
Number of matches
advertisement .
MATCH_NUM_ONE_ADVERTISEMENT
. .
MATCH_NUM_FEW_ADVERTISEMENT
. .
MATCH_NUM_MAX_ADVERTISEMENT
. advertisement , .
. - , 2 .
Report delay
allback . , Android onBatchScanResults
. onScanResult
. , . - , MAC- ( ).
: Samsung S6 / Samsung S6 Edge, RSSI ( ) .
Android Bluetooth
BLE «» Bluetooth . : , MAC-, (, ), (Classic, Dual, BLE) .. Android , . , . . , Android , . - MAC- !
Bluetooth , , , 3 , :
Bluetooth
( )
, , - . , Samsung, Bluetooth.
, BT . , :
// Get device object for a mac address
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(peripheralAddress)
// Check if the peripheral is cached or not
int deviceType = device.getType();
if(deviceType == BluetoothDevice.DEVICE_TYPE_UNKNOWN) {
// The peripheral is not cached
} else {
// The peripheral is cached
}
, , . .
?
– , , , . , BLE-, , (foreground), .
, Google () :
c Android 8.1 .
ScanFilters
, Android , , . Google. Google.
c Android 7 30 , Android
SCAN_MODE_OPPORTUNISTIC
. , , 30 . commit .
Android 7 5 30 .
Google . ! Android , 10 , . :
StackOverflow
David Young
(permissions)
, . (permissions):
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
, , . ACCESS_COARSE_LOCATION
Google «» .
private boolean hasPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (getApplicationContext().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[] { Manifest.permission.ACCESS_COARSE_LOCATION }, ACCESS_COARSE_LOCATION_REQUEST);
return false;
}
}
return true;
}
. , BLE 2 : ACCESS_FINE_LOCATION
( API<23) ACCESS_BACKGROUND_LOCATION
Stackoverflow.
Android10:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
, Bluetooth, - Intent
:
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
BLE Activity (Fragment / Service), , (permissions) Android-Bluetooth . .
!