A complete Android Bluetooth Low Energy (BLE) SDK and robust sample application written entirely in Kotlin. This project focuses on providing a clean, stateful, and reactive API for interacting with BLE devices, with a specific architectural emphasis on separating core Bluetooth logic from UI components.
The project is structured into two main modules to ensure a strict separation of concerns:
ble-sdk: The core BLE library containing all the complex Bluetooth logic, scanning state management, connection handling, and message transactions.app: A sample application built with Jetpack Compose that consumes theble-sdk. It strictly acts as a UI layer proxy, observing states and delegating user intents to the SDK.
- Uses Kotlin Coroutines and StateFlow to provide a reactive stream of discovered devices.
- Retains previously discovered devices during continuous scanning.
- Filters out "Unknown" nameless devices dynamically in real-time without needing to restart the hardware scan.
- Fully manages GATT connections, service discovery, and characteristic communication.
- Exposes real-time connectivity states (
ConnectionState.CONNECTING,CONNECTED,DISCONNECTED). - Supports reading notifications, writing messages, reading RSSI directly from connected peripherals, and disabling notifications.
- Automatically retries connection attempts and provides descriptive logging.
The SDK provides fully typed Coroutine-based helpers and parsing extensions for standard Bluetooth SIG services:
- Heart Rate:
subscribeToHeartRate(),BleExtensions.parseHeartRate() - Blood Pressure:
subscribeToBloodPressure(),BleExtensions.parseBloodPressure() - Health Thermometer:
subscribeToHealthThermometer(),BleExtensions.parseHealthThermometer() - Weight Scale:
subscribeToWeightScale(),BleExtensions.parseWeightMeasurement() - Device Information:
readDeviceManufacturerName(),readBatteryLevel()
- MVVM Architecture: The app's
MainViewModelpurely delegates toBleScannerand exposes flows, keeping the ViewModel extremely thin.
┌─────────────────────────────────┐
│ app module │
│ ┌─────────────────────────┐ │
│ │ MainViewModel (proxy) │ │
│ │ startScan() ──────────────────────┐
│ │ stopScan() ───────────────────┐ │
│ │ setFilterUnknown() ───────┐ │ │
│ │ scannedDevices ←──────────┼───┼───┼──┐
│ └─────────────────────────┘ │ │ │ │
└───────────────────────────────┼───┼───┼──┼──┘
│ │ │ │
┌─────────────────────────────────────────────┐
│ ble-sdk module │ │ │ │
│ ┌──────────────────────────────▼───▼───▼──▼┐
│ │ BleScanner (stateful) │
│ │ _rawDevices, _scannedDevices │
│ │ filterUnknown │
│ └──────────────────────────────────────────┘
└─────────────────────────────────────────────┘
- Dependency Injection: Integrated with Koin.
- Unit Testing: Excellent test coverage in both modules using MockK, Robolectric, and Coroutines Test Dispatchers.
- Android Studio Giraffe or newer
- Minimum SDK: 26
- Target SDK: 34
- Kotlin 1.9+
The sample app automatically requests necessary permissions at runtime depending on the OS version (BLUETOOTH_SCAN, BLUETOOTH_CONNECT, ACCESS_FINE_LOCATION).
Note: Ensure your physical test device has Bluetooth enabled before initiating a scan. The sample app will gracefully display an alert if it is off.
To start a scan and observe devices from your ViewModel:
val bleManager = BleManager(applicationContext)
val bleScanner = bleManager.scanner
// Observe the filtered device list in real-time
val scannedDevices = bleScanner.scannedDevices
// Start continuous background scanning
bleScanner.startScan(viewModelScope)
// Connect to a specific BLE device
val connection = bleManager.connect(device)
connection?.connect()
// GATT operations like discoverServices are suspend functions
viewModelScope.launch {
connection?.discoverServices()
// Example: Subscribe to a standard profile
connection?.subscribeToHeartRate()
}The project is thoroughly tested to ensure thread-safety and proper state propagation across hardware callbacks.
Run tests using Gradle:
./gradlew testTests handle eager coroutine dispatchers to accurately capture and verify standard BluetoothAdapter and GATT callbacks.
This project is open-source and intended for educational and reference purposes regarding correct Android BLE architecture.