Android BLE Connection — Complete Guide
In this tutorial, you'll learn about Android BLE Connection. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
The Problem
Your BLE device connects but immediately disconnects, or connectGatt returns null.
Wrong Approach ❌
// Connecting on the main thread — blocks UI
val gatt = device.connectGatt(context, false, gattCallback)
// Autoconnect = false — doesn't reconnect after disconnection
Output: Connection fails silently. No reconnection on transient disconnects.
Right Approach ✅
class BleConnector(private val context: Context) {
private var gatt: BluetoothGatt? = null
private var connectionState = State.DISCONNECTED
private var reconnectAttempts = 0
private val maxReconnectAttempts = 3
enum class State { DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING }
fun connect(device: BluetoothDevice) {
if (connectionState == State.CONNECTING) return
connectionState = State.CONNECTING
// Autoconnect = true for automatic reconnection
gatt = device.connectGatt(
context,
true, // Auto-reconnect
object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
when (newState) {
BluetoothProfile.STATE_CONNECTED -> {
connectionState = State.CONNECTED
reconnectAttempts = 0
gatt.discoverServices() // Discover after connect
}
BluetoothProfile.STATE_DISCONNECTED -> {
connectionState = State.DISCONNECTED
handleDisconnection(gatt, status)
}
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) {
onServicesReady?.invoke(gatt.services)
} else {
Log.e("BleConnect", "Service discovery failed: $status")
}
}
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d("BleConnect", "MTU updated to $mtu")
}
}
}
)
}
private fun handleDisconnection(gatt: BluetoothGatt, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// Intentional disconnection
return
}
// Unexpected disconnection — attempt reconnect
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++
Log.d("BleConnect", "Reconnecting ($reconnectAttempts/$maxReconnectAttempts)")
// Auto-reconnect is handled by autoconnect = true
} else {
Log.e("BleConnect", "Max reconnection attempts reached")
close()
}
}
fun disconnect() {
connectionState = State.DISCONNECTING
gatt?.disconnect()
}
fun close() {
gatt?.close()
gatt = null
connectionState = State.DISCONNECTED
}
// Request MTU for larger data transfers
fun requestMtu(mtu: Int = 512) {
gatt?.requestMtu(mtu) // Default is 23 bytes
}
var onServicesReady: ((List<BluetoothGattService>) -> Unit)? = null
}
Output: Reliable BLE connection with automatic reconnection.
Prevention
- Use
autoconnect = truefor maintaining connections. - Call
discoverServices()after successful connection. - Request MTU 512 (or higher) for efficient data transfer.
- Handle
status != GATT_SUCCESSinonConnectionStateChange. - Always call
close()to release GATT resources.
Common Mistakes with bluetooth connect
- Misunderstanding that
Stringis[Char]with poor performance for large text operations - Using
foldlinstead offoldl'causing stack overflow on large lists - Forgetting
deriving (Show, Eq)on custom data types needed for debugging
These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro