BluetoothGatt — vad är det, metoder och GATT-protokoll för BLE i Android

Författare: IT Sectr Publicerad: 2026-07-16 Lästid: 10 min

BluetoothGatt — Android-klass som tillhandahåller API för GATT-klienten (Generic Attribute Profile) över BLE-anslutning. BluetoothGatt kapslar in anslutningen till en fjärr-GATT-server (perifer BLE-enhet) och hanterar alla profiloperationer: upptäckt av tjänster, läsning och skrivning av egenskaper, prenumeration på meddelanden och indikationer. En BluetoothGatt-instans erhålls via BluetoothDevice.connectGatt() med callback BluetoothGattCallback. Enligt Android Developers, 2026 är BluetoothGatt den centrala klassen för tvåvägs BLE-kommunikation som stöder GATT-operationer från BLE 4.0 till BLE 5.4.

Huvudpunkter

  • BluetoothGatt — Android-klass för GATT-klient som hanterar BLE-anslutning med perifer enhet
  • connectGatt() — BluetoothDevice-metod för att skapa BluetoothGatt; accepterar kontext, autoConnect, callback och transport
  • discoverServices() — metod för att erhålla GATT-hierarki: tjänster (BluetoothGattService), egenskaper (BluetoothGattCharacteristic)
  • readCharacteristic/writeCharacteristic — läs- och skrivmetoder med asynkront resultat via BluetoothGattCallback
  • setCharacteristicNotification — metod för prenumeration på BLE-meddelanden med obligatorisk skrivning av CCCD-deskriptor

Vad är BluetoothGatt: essens och skapande av anslutning

BluetoothGatt — är ett proxyobjekt som representerar GATT-anslutningen mellan en Android-enhet (central) och BLE-periferi (server). Varje BluetoothGatt-instans motsvarar en aktiv BLE-anslutning. Genom den utförs alla GATT-profiloperationer: upptäckt, läsning, skrivning, meddelanden. BluetoothGatt skapas inte direkt — det returneras av metoden BluetoothDevice.connectGatt().

Att skapa BluetoothGatt kräver fyra parametrar. Context — applikationskontext (Activity eller Application). autoConnect — om false, initierar Android omedelbart direkt anslutning; om true, ansluter Android automatiskt vid upptäckt av enheten (användbart för bakgrundsanslutning). BluetoothGattCallback — obligatorisk callback för alla GATT-händelser. transport — BluetoothDevice.TRANSPORT_LE (BLE) eller TRANSPORT_BREDR (Classic). Använd alltid TRANSPORT_LE på BLE-enheter.

BluetoothGatts livscykel består av fem tillstånd. DISCONNECTED — initialt tillstånd. CONNECTING — efter anrop av connectGatt, fram till bekräftelse. CONNECTED — efter onConnectionStateChange med STATE_CONNECTED. Efter anslutning anropas discoverServices() för att erhålla GATT-hierarkin. Efter avslutat arbete — disconnect() och close() för att frigöra systemresurser. Utan close() kan appen förbruka Android BLE-anslutningsgränsen (vanligtvis 4–8).

kotlin
// Skapa BluetoothGatt-anslutning
import android.bluetooth.*

class GattConnector(private val context: Context) {

    private var bluetoothGatt: BluetoothGatt? = null

    fun connect(device: BluetoothDevice): BluetoothGatt? {
        // Stäng föregående anslutning om den finns
        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. Anslutning upprättad → Upptäckt
                            gatt.discoverServices()
                        }
                        BluetoothProfile.STATE_DISCONNECTED -> {
                            // 3. Anslutning förlorad
                            close()
                        }
                    }
                }

                override fun onServicesDiscovered(
                    gatt: BluetoothGatt, status: Int
                ) {
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        // 4. GATT-hierarki mottagen
                        onGattReady(gatt)
                    }
                }
            },
            BluetoothDevice.TRANSPORT_LE
        )
        return bluetoothGatt
    }

    private fun onGattReady(gatt: BluetoothGatt) {
        // GATT redo för läs-/skrivoperationer
    }

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

Klassen GattConnector demonstrerar korrekt skapande av BluetoothGatt. Parametern autoConnect=false — direkt anslutning (för skannade enheter). Vid onConnectionStateChange med STATE_CONNECTED anropas omedelbart discoverServices(). onServicesDiscovered signalerar GATT-beredskap. close() anropar sekventiellt disconnect() och close() — utan close() frigörs inte systemresurser, vilket leder till läckage av BLE-anslutningar.

Upptäckt av tjänster och egenskaper via BluetoothGatt

discoverServices() — första GATT-metoden som anropas efter anslutning. Initierar asynkron sökning efter alla tjänster på BLE-periferin. Resultatet kommer i onServicesDiscovered() med statuskod: GATT_SUCCESS (0) — lyckad, 133 — GATT_ERROR, 8 — GATT_CONNECTION_TIMEOUT. Efter lyckad upptäckt fyller BluetoothGatt listan över tjänster som är tillgängliga via getServices().

Varje BluetoothGattService innehåller en lista av BluetoothGattCharacteristic. En egenskap har UUID, egenskaper (PROPERTY_READ, PROPERTY_WRITE, PROPERTY_NOTIFY) och valfria deskriptorer. Egenskaperna bestämmer vilka operationer som är tillåtna: om en egenskap saknar PROPERTY_READ kommer anrop av readCharacteristic att returnera ett fel. För att erhålla deskriptorer för en egenskap används getDescriptors().

kotlin
// Tjänstupptäckt och egenskapssökning
class GattServiceExplorer {

    // Hitta tjänst efter UUID
    fun findService(gatt: BluetoothGatt, uuid: UUID): BluetoothGattService? {
        return gatt.services?.firstOrNull { it.uuid == uuid }
    }

    // Hitta egenskap i tjänst
    fun findCharacteristic(
        service: BluetoothGattService,
        uuid: UUID
    ): BluetoothGattCharacteristic? {
        return service.characteristics?.firstOrNull { it.uuid == uuid }
    }

    // Hämta alla stödda egenskapsoperationer
    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
    }

    // Logga hela GATT-hierarkin
    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}")
                }
            }
        }
    }
}

Klassen GattServiceExplorer tillhandahåller verktyg för navigering i GATT-hierarkin. findService och findCharacteristic söker efter tjänster och egenskaper enligt UUID. getCharacteristicProperties kontrollerar bitmasker för egenskaper via and(). dumpGattTree skriver ut hela hierarkin i loggen — användbar vid felsökning av BLE-enheter. Alla operationer på BluetoothGatt måste utföras efter lyckad onServicesDiscovered, annars returnerar getServices() en tom lista.

Läsning av BLE-egenskaper: readCharacteristic och readDescriptor

readCharacteristic() — asynkron BluetoothGatt-metod för att läsa värdet av en egenskap från en fjärr-BLE-enhet. Resultatet kommer i onCharacteristicRead() för BluetoothGattCallback. Om det finns 2+ matchande egenskaper på enheten (osannolikt men möjligt), kan readCharacteristic() läsa en icke-mål-egenskap — säkrare är att anropa readCharacteristic() på BluetoothGattCharacteristic-instansen, inte enligt UUID.

readDescriptor() — metod för att läsa värdet av en deskriptor för en egenskap. En typisk deskriptor — CCCD (Client Characteristic Configuration Descriptor, UUID 0x2902) som bestämmer om meddelanden är aktiverade. Resultat i onDescriptorRead(). Läsning av deskriptorer krävs sällan i praktiken — CCCD hanteras av setCharacteristicNotification(), men för anpassade deskriptorer (User Description 0x2901, Presentation Format 0x2904) är readDescriptor() det enda sättet att erhålla metadata.

MTU och läsning av stora data — om egenskapens värde överstiger MTU (23 byte för BLE 4.0) fragmenterar och assemblerar Android automatiskt data genom en sekvens av läsförfrågningar via BLE-stacken. För BLE 5.0+ med utökad MTU (upp till 251 byte) krävs ingen fragmentering — en läsning returnerar fullständiga data. Före läsning kan requestMtu() anropas för att förhandla om maximal MTU.

kotlin
// Läsning av egenskap och deskriptor via BluetoothGatt
class GattReader {

    fun readHeartRate(gatt: BluetoothGatt) {
        // Heart Rate-tjänst UUID = 0x180D
        val service = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
            ?: return
        // Heart Rate-mätning UUID = 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) }
    }

    // Bearbeta data i onCharacteristicRead-callback:
    fun parseHeartRate(value: ByteArray): Int {
        // BLE Heart Rate: byte = flags, andra = 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
    }
}

Klassen GattReader demonstrerar läsning av Heart Rate-egenskapen. Tjänsten 0x180D innehåller egenskapen 0x2A37 (Heart Rate Measurement) — standard BLE-profil för Bluetooth SIG. Före läsning kontrolleras egenskapen PROPERTY_READ via hasProperty. parseHeartRate parses BLE-pulsformatet: första byten — flags (dataformat), andra — bpm-värde. CCCD-deskriptorn (0x2902) läses för att kontrollera meddelandestatus.

Skrivning av egenskaper: writeCharacteristic med WriteType

writeCharacteristic() — BluetoothGatt-metod för att skriva data till BLE-periferi. På Android API 33+ har writeCharacteristic() ersatts av writeCharacteristic(request), där BluetoothGattCharacteristicWriteRequest är ett förfrågningsobjekt som innehåller egenskapen, en byte-array och WriteType. Den gamla metoden writeCharacteristic(characteristic) med setValue() är föråldrad. WriteType bestämmer förfrågans beteende: WRITE_TYPE_DEFAULT (beror på egenskapens egenskaper), WRITE_TYPE_NO_RESPONSE (withoutResponse) och WRITE_TYPE_SIGNED (autorisering).

Val av WriteType påverkar hastighet och tillförlitlighet. WRITE_TYPE_DEFAULT motsvarar vanligtvis withResponse (om egenskapen har PROPERTY_WRITE) eller withoutResponse (om den har PROPERTY_WRITE_NO_RESPONSE). För strömmande data (OTA-uppdateringar, loggar) använd WRITE_TYPE_NO_RESPONSE — maximal bandbredd. För kommandon med leveransgaranti (aktivering, konfiguration) — WRITE_TYPE_DEFAULT med bekräftelse via onCharacteristicWrite.

kotlin
// BLE-egenskapsskrivning på Android API 33+
class GattWriter {

    // Skrivning med svar (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)
        }
    }

    // Skrivning utan svar (maximal hastighet)
    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)
        }
    }

    // onCharacteristicWrite-callback (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")
            }
        }
    }
}

Klassen GattWriter stöder båda WriteType för olika API-nivåer. writeWithResponse använder WRITE_TYPE_DEFAULT — BLE-enheten bekräftar skrivning via onCharacteristicWrite. writeWithoutResponse använder WRITE_TYPE_NO_RESPONSE — data skickas utan bekräftelse, maximal bandbredd. På API 33+ används den nya writeCharacteristic(request) med BluetoothGattCharacteristicWriteRequest. På API < 33 — gamla setValue() + writeCharacteristic().

Prenumeration på meddelanden: setCharacteristicNotification och CCCD

setCharacteristicNotification() — BluetoothGatt-metod för prenumeration på meddelanden om ändring av egenskap på periferin. Efter aktivering av prenumeration skickar BLE-enheten nya värden via onCharacteristicChanged(). Men setCharacteristicNotification() aktiverar endast den lokala Android-meddelandet — för att aktivera meddelanden på själva BLE-enheten måste även värdet 0x0100 skrivas till CCCD-deskriptorn (0x2902).

CCCD (Client Characteristic Configuration Descriptor) — deskriptor som hanterar sändning av meddelanden från BLE-periferin. Värde 0x0000 — meddelanden avaktiverade, 0x0100 — meddelanden aktiverade (notifications), 0x0200 — indikationer aktiverade (indications). Skrivning till CCCD utförs via writeDescriptor() på BluetoothGatt efter anrop av setCharacteristicNotification(). Android skriver inte CCCD automatiskt — detta ansvar ligger på utvecklaren.

kotlin
// Korrekt BLE-meddelandeprenumeration
class GattNotificationManager {

    // 1. Aktivera meddelanden
    fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
        // Steg 1: lokal Android-prenumeration
        val success = gatt.setCharacteristicNotification(characteristic, true)
        if (!success) {
            print("Prenumeration misslyckades")
            return
        }

        // Steg 2: skriv CCCD (0x2902) på BLE-enheten
        val cccdDescriptor = characteristic.getDescriptor(
            UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
        ) ?: return

        // 0x0100 = meddelande, 0x0200 = indikation
        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. Egenskaper upptäckt
    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. Meddelandecallback
    private val notificationCallback = object : BluetoothGattCallback() {
        override fun onCharacteristicChanged(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            value: ByteArray,
            callbackType: Int
        ) {
            // Nytt värde från BLE-periferi
            print("Notification: ${value.size} bytes")
        }
    }
}

Klassen GattNotificationManager implementerar korrekt tvåstegsprotokoll för prenumeration på BLE-meddelanden. enableNotification anropar först setCharacteristicNotification(true) på Android, skriver sedan 0x0100 till CCCD-deskriptorn via writeDescriptor. disableNotification utför motsatta operationer. Utan CCCD-skrivning skickar BLE-enheten inga meddelanden — detta är det vanligaste felet bland BLE-utvecklare på Android.

Exempel på GATT-klient i Kotlin med BluetoothGattCallback

Fullständigt exempel på GATT-klient i Kotlin som kombinerar skapande av BluetoothGatt, upptäckt, läsning och prenumeration på meddelanden i en enda hanterare med hjälp av korutiner för asynkron bearbetning.

kotlin
// Fullständig GATT-klient med korutiner i Kotlin
class GattClient(context: Context) {

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

    // Anslut med korutin
    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
            )
        }

    // Läs egenskap via korutin
    suspend fun readCharacteristicValue(char: BluetoothGattCharacteristic): ByteArray? =
        suspendCoroutine { continuation ->
            gatt?.let { gatt ->
                // Spara egenskapstagg för callback-identifiering
                gatt.setCharacteristic(char, null)  // för API < 33
                gatt.readCharacteristic(char)
            }
        }

    // Stäng anslutning
    fun release() {
        gatt?.disconnect()
        gatt?.close()
        gatt = null
    }
}

GATT-klienten GattClient använder korutiner (suspendCoroutine) för att omvandla det callback-baserade BluetoothGatt API till sekventiella anrop. connect() väntar på onServicesDiscovered, varefter GATT-hierarkin är tillgänglig. readCharacteristicValue() väntar på onCharacteristicRead. Detta tillvägagångssätt eliminerar nästlade callbacks och gör BLE-koden linjär. release() garanterar frigöring av resurser — obligatoriskt anrop i onDestroy för Activity eller ViewModel.onCleared.

Vanliga frågor

Vad är BluetoothGatt i Android?

BluetoothGatt — klass för Android GATT-klient som hanterar BLE-anslutning med perifer enhet. Skapas via BluetoothDevice.connectGatt(), tillhandahåller metoder discoverServices(), readCharacteristic(), writeCharacteristic(), setCharacteristicNotification(). Resultat av alla operationer kommer asynkront via BluetoothGattCallback. Utan BluetoothGatt är tvåvägs BLE-kommunikation på Android omöjlig.

Varför returnerar onServicesDiscovered status 133?

Status 133 (GATT_ERROR) betyder ett internt fel i Android BLE-stacken. Orsaker: enheten kopplades bort under upptäckt, MTU är mindre än minimum (23 byte) eller BLE-stacken är överbelastad. Lösning: upprepa discoverServices() med en fördröjning på 500 ms, kontrollera enhetens RSSI och säkerställ att periferin stöder GATT-upptäckt i aktuellt tillstånd.

Hur skriver jag korrekt en egenskap med bekräftelse?

För skrivning med bekräftelse, anropa writeCharacteristic() med WRITE_TYPE_DEFAULT (API 33+: BluetoothGattCharacteristicWriteRequest). Vid framgång skickar BLE-enheten en bekräftelse och Android anropar onCharacteristicWrite med GATT_SUCCESS. Om enheten inte svarar inom 30 sekunder (stack-timeout) returnerar callbacken en felstatus. För watchdog, använd Handler med postDelayed.

Hur arbetar jag med BLE på Android 33+?

På Android 13+ (API 33) har BluetoothGatt-metoder ändrats: writeCharacteristic() accepterar nu BluetoothGattCharacteristicWriteRequest, readCharacteristic() — BluetoothGattCharacteristicReadRequest. Gamla setValue()/writeCharacteristic() är föråldrade. BluetoothGattCallback har också ändrats: onCharacteristicRead(), onCharacteristicWrite(), onCharacteristicChanged() får ByteArray value och callbackType. Använd Build.VERSION.SDK_INT för förgrening.

Hur många BLE-anslutningar stöder Android?

Android stöder 4–8 samtidiga BLE-GATT-anslutningar (beroende på tillverkare och Android-version). Pixel/Google: upp till 7, Samsung: upp till 5, Xiaomi: upp till 4. Vid överskridande av gränsen returnerar connectGatt null eller onConnectionStateChange med fel. För arbete med många enheter, använd cyklisk anslutning eller Bluetooth Mesh.

Sammanfattning

  • BluetoothGatt — Android GATT-klient för BLE-anslutning, skapad via BluetoothDevice.connectGatt() med BluetoothGattCallback
  • discoverServices() — obligatoriskt steg efter anslutning för att erhålla tjänster, egenskaper och deskriptorer för BLE-enheten
  • Läsning — readCharacteristic() med asynkront resultat i onCharacteristicRead(); för stora data krävs MTU-förhandling
  • Skrivning — writeCharacteristic() med WriteType: DEFAULT (withResponse) eller NO_RESPONSE (utan bekräftelse)
  • Meddelanden — tvåstegsaktivering: setCharacteristicNotification() + skrivning av CCCD (0x2902) med värdet 0x0100
  • API 33+ — nya metoder writeCharacteristic(request) och readCharacteristic(request) med förfrågningsobjekt
  • Resursfrigöring — obligatoriskt anrop av disconnect() och close() för att förhindra läckage av BLE-anslutningar

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också