BluetoothGatt — co to je, metody a GATT protokol BLE v Androidu

Autor: IT Sectr Publikováno: 2026-07-16 Doba čtení: 10 min

BluetoothGatt — třída Android poskytující API pro práci GATT klienta (Generic Attribute Profile) přes BLE spojení. BluetoothGatt zapouzdřuje připojení ke vzdálenému GATT serveru (perifernímu BLE zařízení) a spravuje všechny operace profilu: objevování služeb, čtení a zápis charakteristik, přihlášení k odběru oznámení a indikací. Instance BluetoothGatt se získává pomocí BluetoothDevice.connectGatt() s callbackem BluetoothGattCallback. Podle Android Developers, 2026 je BluetoothGatt centrální třídou pro obousměrnou BLE komunikaci, podporující GATT operace od BLE 4.0 do BLE 5.4.

Hlavní body

  • BluetoothGatt — třída Android pro GATT klienta spravující BLE spojení s periferním zařízením
  • connectGatt() — metoda BluetoothDevice pro vytvoření BluetoothGatt; přijímá kontext, autoConnect, callback a transport
  • discoverServices() — metoda pro získání GATT hierarchie: služby (BluetoothGattService), charakteristiky (BluetoothGattCharacteristic)
  • readCharacteristic/writeCharacteristic — metody čtení a zápisu s asynchronním výsledkem přes BluetoothGattCallback
  • setCharacteristicNotification — metoda přihlášení k odběru BLE oznámení s povinným zápisem CCCD deskriptoru

Co je BluetoothGatt: podstata a vytvoření spojení

BluetoothGatt — je proxy objekt představující GATT spojení mezi zařízením Android (centrála) a BLE periferií (server). Každá instance BluetoothGatt odpovídá jednomu aktivnímu BLE spojení. Prostřednictvím něj se provádějí všechny operace GATT profilu: objevování, čtení, zápis, oznámení. BluetoothGatt se nevytváří přímo — je vrácen metodou BluetoothDevice.connectGatt().

Vytvoření BluetoothGatt vyžaduje čtyři parametry. Context — kontext aplikace (Activity nebo Application). autoConnect — pokud false, Android okamžitě zahájí přímé připojení; pokud true, Android se připojí automaticky při detekci zařízení (užitečné pro připojení na pozadí). BluetoothGattCallback — povinný callback pro všechny události GATT. transport — BluetoothDevice.TRANSPORT_LE (BLE) nebo TRANSPORT_BREDR (Classic). Na BLE zařízeních vždy používejte TRANSPORT_LE.

Životní cyklus BluetoothGatt se skládá z pěti stavů. DISCONNECTED — počáteční stav. CONNECTING — po zavolání connectGatt, do potvrzení. CONNECTED — po onConnectionStateChange s STATE_CONNECTED. Po připojení se volá discoverServices() pro získání GATT hierarchie. Po dokončení práce — disconnect() a close() pro uvolnění systémových prostředků. Bez zavolání close() může aplikace vyčerpat limit BLE připojení Androidu (obvykle 4–8).

kotlin
// Vytvoření BluetoothGatt připojení
import android.bluetooth.*

class GattConnector(private val context: Context) {

    private var bluetoothGatt: BluetoothGatt? = null

    fun connect(device: BluetoothDevice): BluetoothGatt? {
        // Zavřít předchozí připojení pokud existuje
        close()

        bluetoothGatt = device.connectGatt(
            context,
            false,  // autoConnect = false ( )
            object : BluetoothGattCallback() {
                override fun onConnectionStateChange(
                    gatt: BluetoothGatt, status: Int, newState: Int
                ) {
                    when (newState) {
                        BluetoothProfile.STATE_CONNECTED -> {
                            // 2. Připojení navázáno → Objevování
                            gatt.discoverServices()
                        }
                        BluetoothProfile.STATE_DISCONNECTED -> {
                            // 3. Připojení ztraceno
                            close()
                        }
                    }
                }

                override fun onServicesDiscovered(
                    gatt: BluetoothGatt, status: Int
                ) {
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        // 4. GATT hierarchie přijata
                        onGattReady(gatt)
                    }
                }
            },
            BluetoothDevice.TRANSPORT_LE
        )
        return bluetoothGatt
    }

    private fun onGattReady(gatt: BluetoothGatt) {
        // GATT připraven pro operace čtení/zápisu
    }

    fun close() {
        bluetoothGatt?.disconnect()
        bluetoothGatt?.close()
        bluetoothGatt = null
    }
}

Třída GattConnector demonstruje správné vytvoření BluetoothGatt. Parametr autoConnect=false — přímé připojení (pro naskenovaná zařízení). Při onConnectionStateChange se STATE_CONNECTED se okamžitě volá discoverServices(). onServicesDiscovered signalizuje připravenost GATT. close() postupně volá disconnect() a close() — bez close() se systémové prostředky neuvolňují, což vede k úniku BLE připojení.

Objevování služeb a charakteristik přes BluetoothGatt

discoverServices() — první GATT metoda volaná po připojení. Iniciuje asynchronní vyhledávání všech služeb na BLE periferii. Výsledek přichází v onServicesDiscovered() s kódem stavu: GATT_SUCCESS (0) — úspěch, 133 — GATT_ERROR, 8 — GATT_CONNECTION_TIMEOUT. Po úspěšném objevení BluetoothGatt naplní seznam služeb dostupných přes getServices().

Každý BluetoothGattService obsahuje seznam BluetoothGattCharacteristic. Charakteristika má UUID, vlastnosti (PROPERTY_READ, PROPERTY_WRITE, PROPERTY_NOTIFY) a volitelné deskriptory. Vlastnosti určují, které operace jsou povoleny: pokud charakteristika nemá PROPERTY_READ, volání readCharacteristic vrátí chybu. Pro získání deskriptorů charakteristiky se používá getDescriptors().

kotlin
// Objevování služeb a vyhledávání charakteristik
class GattServiceExplorer {

    // Najít službu podle UUID
    fun findService(gatt: BluetoothGatt, uuid: UUID): BluetoothGattService? {
        return gatt.services?.firstOrNull { it.uuid == uuid }
    }

    // Najít charakteristiku ve službě
    fun findCharacteristic(
        service: BluetoothGattService,
        uuid: UUID
    ): BluetoothGattCharacteristic? {
        return service.characteristics?.firstOrNull { it.uuid == uuid }
    }

    // Získat všechny podporované operace charakteristiky
    fun getCharacteristicProperties(chars: BluetoothGattCharacteristic): List<String> {
        val props = mutableListOf<String>()
        with(chars.properties) {
            if (and(BluetoothGattCharacteristic.PROPERTY_READ) != 0) props.add("READ")
            if (and(BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) props.add("WRITE")
            if (and(BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0) props.add("WRITE_NO_RESP")
            if (and(BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) props.add("NOTIFY")
            if (and(BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) props.add("INDICATE")
        }
        return props
    }

    // Zalogovat celou GATT hierarchii
    fun dumpGattTree(gatt: BluetoothGatt) {
        gatt.services?.forEach { service ->
            print("Service: ${service.uuid}")
            service.characteristics?.forEach { char ->
                print("  Characteristic: ${char.uuid}, properties: ${char.properties}")
                char.descriptors?.forEach { desc ->
                    print("    Descriptor: ${desc.uuid}")
                }
            }
        }
    }
}

Třída GattServiceExplorer poskytuje nástroje pro navigaci v GATT hierarchii. findService a findCharacteristic vyhledávají služby a charakteristiky podle UUID. getCharacteristicProperties kontroluje bitové masky vlastností přes and(). dumpGattTree vypisuje celou hierarchii do logu — užitečné při ladění BLE zařízení. Všechny operace na BluetoothGatt musí být provedeny po úspěšném onServicesDiscovered, jinak getServices() vrátí prázdný seznam.

Čtení BLE charakteristik: readCharacteristic a readDescriptor

readCharacteristic() — asynchronní metoda BluetoothGatt pro čtení hodnoty charakteristiky ze vzdáleného BLE zařízení. Výsledek přichází v onCharacteristicRead() BluetoothGattCallback. Pokud jsou na zařízení 2+ odpovídající charakteristiky (nepravděpodobné, ale možné), readCharacteristic() může přečíst necílovou — bezpečnější je volat readCharacteristic() na instanci BluetoothGattCharacteristic, ne podle UUID.

readDescriptor() — metoda pro čtení hodnoty deskriptoru charakteristiky. Typický deskriptor — CCCD (Client Characteristic Configuration Descriptor, UUID 0x2902) určující, zda jsou oznámení zapnuta. Výsledek v onDescriptorRead(). Čtení deskriptorů je v praxi zřídka vyžadováno — CCCD je spravován setCharacteristicNotification(), ale pro vlastní deskriptory (User Description 0x2901, Presentation Format 0x2904) je readDescriptor() jediným způsobem získání metadat.

MTU a čtení velkých dat — pokud hodnota charakteristiky přesahuje MTU (23 bajtů pro BLE 4.0), Android automaticky fragmentuje a sestavuje data sekvencí čtecích požadavků přes BLE stack. Pro BLE 5.0+ s rozšířeným MTU (až 251 bajtů) není fragmentace vyžadována — jedno čtení vrací kompletní data. Před čtením lze zavolat requestMtu() pro dohodnutí maximálního MTU.

kotlin
// Čtení charakteristiky a deskriptoru přes BluetoothGatt
class GattReader {

    fun readHeartRate(gatt: BluetoothGatt) {
        // UUID služby Heart Rate = 0x180D
        val service = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
            ?: return
        // UUID měření Heart Rate = 0x2A37
        val characteristic = service.getCharacteristic(
            UUID.fromString("00002A37-0000-1000-8000-00805F9B34FB")
        ) ?: return

        if (hasProperty(characteristic, BluetoothGattCharacteristic.PROPERTY_READ)) {
            gatt.readCharacteristic(characteristic)
        }
    }

    fun readDescriptor(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
        // CCCD (0x2902)
        val cccd = characteristic.getDescriptor(
            UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
        )
        cccd?.let { gatt.readDescriptor(it) }
    }

    // Zpracovat data v callbacku onCharacteristicRead:
    fun parseHeartRate(value: ByteArray): Int {
        // BLE Heart Rate: bajty = flags, druhý = bpm
        return if (value.isNotEmpty()) value[1].toInt() and 0xFF else 0
    }

    private fun hasProperty(char: BluetoothGattCharacteristic, prop: Int): Boolean {
        return char.properties and prop != 0
    }
}

Třída GattReader demonstruje čtení charakteristiky Heart Rate. Služba 0x180D obsahuje charakteristiku 0x2A37 (Heart Rate Measurement) — standardní BLE profil Bluetooth SIG. Před čtením se kontroluje vlastnost PROPERTY_READ přes hasProperty. parseHeartRate parsuje BLE formát tepové frekvence: první bajt — flags (formát dat), druhý — hodnota bpm. CCCD deskriptor (0x2902) se čte pro kontrolu stavu oznámení.

Zápis charakteristik: writeCharacteristic s WriteType

writeCharacteristic() — metoda BluetoothGatt pro zápis dat na BLE periferii. Na Android API 33+ byl writeCharacteristic() nahrazen writeCharacteristic(request), kde BluetoothGattCharacteristicWriteRequest je objekt požadavku obsahující charakteristiku, pole bajtů a WriteType. Stará metoda writeCharacteristic(characteristic) se setValue() je zastaralá. WriteType určuje chování požadavku: WRITE_TYPE_DEFAULT (závisí na vlastnostech charakteristiky), WRITE_TYPE_NO_RESPONSE (withoutResponse) a WRITE_TYPE_SIGNED (autorizace).

Výběr WriteType ovlivňuje rychlost a spolehlivost. WRITE_TYPE_DEFAULT obvykle odpovídá withResponse (pokud má charakteristika PROPERTY_WRITE) nebo withoutResponse (pokud má PROPERTY_WRITE_NO_RESPONSE). Pro proudová data (OTA aktualizace, logy) použijte WRITE_TYPE_NO_RESPONSE — maximální propustnost. Pro příkazy s garancí doručení (zapnutí, konfigurace) — WRITE_TYPE_DEFAULT s potvrzením přes onCharacteristicWrite.

kotlin
// Zápis BLE charakteristiky na Android API 33+
class GattWriter {

    // Zápis s odpovědí (withResponse)
    fun writeWithResponse(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, data: ByteArray) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            // API 33+: writeCharacteristic
            val request = BluetoothGattCharacteristicWriteRequest(
                characteristic,
                data,
                BluetoothGattCharacteristicWriteRequest.WRITE_TYPE_DEFAULT,
                @android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
            )
            gatt.writeCharacteristic(request)
        } else {
            // API < 33:   (deprecated)
            characteristic.setValue(data)
            gatt.writeCharacteristic(characteristic)
        }
    }

    // Zápis bez odpovědi (maximální rychlost)
    fun writeWithoutResponse(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, data: ByteArray) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            val request = BluetoothGattCharacteristicWriteRequest(
                characteristic,
                data,
                BluetoothGattCharacteristicWriteRequest.WRITE_TYPE_NO_RESPONSE,
                @android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
            )
            gatt.writeCharacteristic(request)
        } else {
            characteristic.setValue(data)
            characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
            gatt.writeCharacteristic(characteristic)
        }
    }

    // Callback onCharacteristicWrite (API 33+)
    private val writeCallback = object : BluetoothGattCallback() {
        override fun onCharacteristicWrite(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            value: ByteArray,
            status: Int,
            callbackType: Int
        ) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                print("Write success: ${value.size} bytes")
            }
        }
    }
}

Třída GattWriter podporuje oba WriteType pro různé úrovně API. writeWithResponse používá WRITE_TYPE_DEFAULT — BLE zařízení potvrzuje zápis přes onCharacteristicWrite. writeWithoutResponse používá WRITE_TYPE_NO_RESPONSE — data jsou odeslána bez potvrzení, maximální propustnost. Na API 33+ se používá nový writeCharacteristic(request) s BluetoothGattCharacteristicWriteRequest. Na API < 33 — starý setValue() + writeCharacteristic().

Přihlášení k odběru oznámení: setCharacteristicNotification a CCCD

setCharacteristicNotification() — metoda BluetoothGatt pro přihlášení k odběru oznámení o změně charakteristiky na periferii. Po aktivaci odběru zařízení BLE odesílá nové hodnoty přes onCharacteristicChanged(). Nicméně setCharacteristicNotification() pouze aktivuje lokální oznámení Androidu — pro zapnutí oznámení na samotném BLE zařízení je také nutné zapsat hodnotu 0x0100 do CCCD deskriptoru (0x2902).

CCCD (Client Characteristic Configuration Descriptor) — deskriptor spravující odesílání oznámení z BLE periferie. Hodnota 0x0000 — oznámení vypnuta, 0x0100 — oznámení zapnuta (notifications), 0x0200 — indikace zapnuta (indications). Zápis do CCCD se provádí přes writeDescriptor() na BluetoothGatt po zavolání setCharacteristicNotification(). Android nezapisuje CCCD automaticky — tato povinnost leží na vývojáři.

kotlin
// Správné přihlášení k odběru BLE oznámení
class GattNotificationManager {

    // 1. Povolit oznámení
    fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
        // Krok 1: lokální odběr Android
        val success = gatt.setCharacteristicNotification(characteristic, true)
        if (!success) {
            print("Přihlášení k odběru selhalo")
            return
        }

        // Krok 2: zapsat CCCD (0x2902) na BLE zařízení
        val cccdDescriptor = characteristic.getDescriptor(
            UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
        ) ?: return

        // 0x0100 = oznámení, 0x0200 = indikace
        val cccdValue = if (characteristic.properties
            and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) {
            BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE  // [0x01, 0x00]
        } else {
            BluetoothGattDescriptor.ENABLE_INDICATION_VALUE     // [0x02, 0x00]
        }

        cccdDescriptor.setValue(cccdValue)
        gatt.writeDescriptor(cccdDescriptor)
    }

    // 2. Objevení charakteristiky
    fun disableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
        gatt.setCharacteristicNotification(characteristic, false)
        val cccdDescriptor = characteristic.getDescriptor(
            UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
        ) ?: return
        cccdDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)
        gatt.writeDescriptor(cccdDescriptor)
    }

    // 3. Callback oznámení
    private val notificationCallback = object : BluetoothGattCallback() {
        override fun onCharacteristicChanged(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            value: ByteArray,
            callbackType: Int
        ) {
            // Nová hodnota z BLE periferie
            print("Notification: ${value.size} bytes")
        }
    }
}

Třída GattNotificationManager implementuje správný dvoukrokový protokol pro přihlášení k odběru BLE oznámení. enableNotification nejprve zavolá setCharacteristicNotification(true) na Androidu, poté zapíše 0x0100 do CCCD deskriptoru přes writeDescriptor. disableNotification provádí opačné operace. Bez zápisu CCCD BLE zařízení neodesílá oznámení — to je nejčastější chyba BLE vývojářů na Androidu.

Příklad GATT klienta v Kotlinu s BluetoothGattCallback

Plný příklad GATT klienta v Kotlinu, který spojuje vytvoření BluetoothGatt, objevování, čtení a přihlášení k odběru oznámení v jediném správci pomocí korutin pro asynchronní zpracování.

kotlin
// Plný GATT klient s korutinami v Kotlinu
class GattClient(context: Context) {

    private val context = context.applicationContext
    private var gatt: BluetoothGatt? = null

    // Připojit s korutinou
    suspend fun connect(device: BluetoothDevice): Boolean =
        suspendCoroutine { continuation ->
            gatt = device.connectGatt(
                context, false,
                object : BluetoothGattCallback() {
                    override fun onConnectionStateChange(
                        gatt: BluetoothGatt, status: Int, newState: Int
                    ) {
                        if (newState == BluetoothProfile.STATE_CONNECTED) {
                            gatt.discoverServices()
                        } else {
                            continuation.resume(false)
                        }
                    }

                    override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
                        continuation.resume(status == BluetoothGatt.GATT_SUCCESS)
                    }
                },
                BluetoothDevice.TRANSPORT_LE
            )
        }

    // Číst charakteristiku přes korutinu
    suspend fun readCharacteristicValue(char: BluetoothGattCharacteristic): ByteArray? =
        suspendCoroutine { continuation ->
            gatt?.let { gatt ->
                // Uložit tag charakteristiky pro identifikaci callbacku
                gatt.setCharacteristic(char, null)  // pro API < 33
                gatt.readCharacteristic(char)
            }
        }

    // Zavřít připojení
    fun release() {
        gatt?.disconnect()
        gatt?.close()
        gatt = null
    }
}

GATT klient GattClient používá korutiny (suspendCoroutine) k převedení callbackového API BluetoothGatt na sekvenční volání. connect() čeká na onServicesDiscovered, poté je GATT hierarchie dostupná. readCharacteristicValue() čeká na onCharacteristicRead. Tento přístup odstraňuje vnořené callbacky a činí BLE kód lineárním. release() zaručuje uvolnění prostředků — povinné volání v onDestroy Activity nebo ViewModel.onCleared.

Často kladené otázky

Co je BluetoothGatt v Androidu?

BluetoothGatt — třída pro GATT klienta Androidu spravující BLE spojení s periferním zařízením. Vytváří se přes BluetoothDevice.connectGatt(), poskytuje metody discoverServices(), readCharacteristic(), writeCharacteristic(), setCharacteristicNotification(). Výsledky všech operací přicházejí asynchronně přes BluetoothGattCallback. Bez BluetoothGatt je obousměrná BLE komunikace na Androidu nemožná.

Proč onServicesDiscovered vrací status 133?

Status 133 (GATT_ERROR) znamená vnitřní chybu BLE stacku Androidu. Příčiny: zařízení se odpojilo během objevování, MTU je menší než minimální (23 bajtů) nebo je BLE stack přetížen. Řešení: opakujte discoverServices() se zpožděním 500 ms, zkontrolujte RSSI zařízení a ujistěte se, že periferie podporuje GATT objevování v aktuálním stavu.

Jak správně zapsat charakteristiku s potvrzením?

Pro zápis s potvrzením zavolejte writeCharacteristic() s WRITE_TYPE_DEFAULT (API 33+: BluetoothGattCharacteristicWriteRequest). Při úspěchu BLE zařízení odešle potvrzení a Android zavolá onCharacteristicWrite s GATT_SUCCESS. Pokud zařízení neodpoví do 30 sekund (timeout stacku), callback vrátí chybový status. Pro watchdog použijte Handler s postDelayed.

Jak pracovat s BLE na Android 33+?

Na Android 13+ (API 33) byly změněny metody BluetoothGatt: writeCharacteristic() nyní přijímá BluetoothGattCharacteristicWriteRequest, readCharacteristic() — BluetoothGattCharacteristicReadRequest. Staré setValue()/writeCharacteristic() jsou zastaralé. Také byl změněn BluetoothGattCallback: onCharacteristicRead(), onCharacteristicWrite(), onCharacteristicChanged() dostávají ByteArray value a callbackType. Pro větvení použijte Build.VERSION.SDK_INT.

Kolik BLE připojení podporuje Android?

Android podporuje 4–8 současných BLE-GATT připojení (závisí na výrobci a verzi Androidu). Pixel/Google: až 7, Samsung: až 5, Xiaomi: až 4. Při překročení limitu connectGatt vrací null nebo onConnectionStateChange s chybou. Pro práci s velkým počtem zařízení použijte cyklické připojení nebo Bluetooth Mesh.

Shrnutí

  • BluetoothGatt — GATT klient Androidu pro BLE připojení, vytvořený přes BluetoothDevice.connectGatt() s BluetoothGattCallback
  • discoverServices() — povinný krok po připojení pro získání služeb, charakteristik a deskriptorů BLE zařízení
  • Čtení — readCharacteristic() s asynchronním výsledkem v onCharacteristicRead(); pro velká data je vyžadováno dohodnutí MTU
  • Zápis — writeCharacteristic() s WriteType: DEFAULT (withResponse) nebo NO_RESPONSE (bez potvrzení)
  • Oznámení — dvoukroková aktivace: setCharacteristicNotification() + zápis CCCD (0x2902) hodnotou 0x0100
  • API 33+ — nové metody writeCharacteristic(request) a readCharacteristic(request) s objekty požadavků
  • Uvolnění prostředků — povinné volání disconnect() a close() pro prevenci úniku BLE připojení

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také