BluetoothGatt — τι είναι, μέθοδοι και πρωτόκολλο GATT BLE σε Android

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-07-16 Χρόνος ανάγνωσης: 10 λεπ

BluetoothGatt — κλάση Android που παρέχει API για τη λειτουργία GATT-πελάτη (Generic Attribute Profile) μέσω σύνδεσης BLE. Το BluetoothGatt ενσωματώνει τη σύνδεση σε απομακρυσμένο GATT-διακομιστή (περιφερειακή συσκευή BLE) και διαχειρίζεται όλες τις λειτουργίες προφίλ: ανακάλυψη υπηρεσιών, ανάγνωση και εγγραφή χαρακτηριστικών, εγγραφή σε ειδοποιήσεις και ενδείξεις. Το στιγμιότυπο BluetoothGatt λαμβάνεται μέσω BluetoothDevice.connectGatt() με επανάκληση BluetoothGattCallback. Σύμφωνα με το Android Developers, 2026, το BluetoothGatt είναι η κεντρική κλάση για αμφίδρομη επικοινωνία BLE, υποστηρίζοντας λειτουργίες GATT από BLE 4.0 έως BLE 5.4.

Κύρια σημεία

  • BluetoothGatt — κλάση Android για GATT-πελάτη που διαχειρίζεται σύνδεση BLE με περιφερειακή συσκευή
  • connectGatt() — μέθοδος BluetoothDevice για δημιουργία BluetoothGatt· δέχεται πλαίσιο, autoConnect, επανάκληση και μεταφορά
  • discoverServices() — μέθοδος για απόκτηση ιεραρχίας GATT: υπηρεσίες (BluetoothGattService), χαρακτηριστικά (BluetoothGattCharacteristic)
  • readCharacteristic/writeCharacteristic — μέθοδοι ανάγνωσης και εγγραφής με ασύγχρονο αποτέλεσμα μέσω BluetoothGattCallback
  • setCharacteristicNotification — μέθοδος εγγραφής σε ειδοποιήσεις BLE με υποχρεωτική εγγραφή περιγραφέα CCCD

Τι είναι το BluetoothGatt: ουσία και δημιουργία σύνδεσης

BluetoothGatt — είναι ένα αντικείμενο proxy που αντιπροσωπεύει τη σύνδεση GATT μεταξύ συσκευής Android (κέντρο) και περιφέρειας BLE (διακομιστής). Κάθε στιγμιότυπο BluetoothGatt αντιστοιχεί σε μία ενεργή σύνδεση BLE. Μέσω αυτού εκτελούνται όλες οι λειτουργίες προφίλ GATT: ανακάλυψη, ανάγνωση, εγγραφή, ειδοποιήσεις. Το BluetoothGatt δεν δημιουργείται άμεσα — επιστρέφεται από τη μέθοδο BluetoothDevice.connectGatt().

Η δημιουργία BluetoothGatt απαιτεί τέσσερις παραμέτρους. Context — πλαίσιο εφαρμογής (Activity ή Application). autoConnect — αν false, το Android ξεκινά αμέσως άμεση σύνδεση· αν true, το Android συνδέεται αυτόματα κατά τον εντοπισμό συσκευής (χρήσιμο για σύνδεση παρασκηνίου). BluetoothGattCallback — υποχρεωτική επανάκληση για όλα τα συμβάντα GATT. transport — BluetoothDevice.TRANSPORT_LE (BLE) ή TRANSPORT_BREDR (Classic). Σε συσκευές BLE χρησιμοποιείτε πάντα TRANSPORT_LE.

Κύκλος ζωής BluetoothGatt αποτελείται από πέντε καταστάσεις. DISCONNECTED — αρχική κατάσταση. CONNECTING — μετά την κλήση connectGatt, έως την επιβεβαίωση. CONNECTED — μετά το onConnectionStateChange με STATE_CONNECTED. Μετά τη σύνδεση καλείται το discoverServices() για απόκτηση της ιεραρχίας GATT. Μετά την ολοκλήρωση — disconnect() και close() για απελευθέρωση πόρων συστήματος. Χωρίς close(), η εφαρμογή μπορεί να εξαντλήσει το όριο συνδέσεων BLE Android (συνήθως 4–8).

kotlin
// Δημιουργία σύνδεσης BluetoothGatt
import android.bluetooth.*

class GattConnector(private val context: Context) {

    private var bluetoothGatt: BluetoothGatt? = null

    fun connect(device: BluetoothDevice): BluetoothGatt? {
        // Κλείσιμο προηγούμενης σύνδεσης εάν υπάρχει
        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. Σύνδεση εγκαταστάθηκε → Ανακάλυψη
                            gatt.discoverServices()
                        }
                        BluetoothProfile.STATE_DISCONNECTED -> {
                            // 3. Σύνδεση χάθηκε
                            close()
                        }
                    }
                }

                override fun onServicesDiscovered(
                    gatt: BluetoothGatt, status: Int
                ) {
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        // 4. Ιεραρχία GATT ελήφθη
                        onGattReady(gatt)
                    }
                }
            },
            BluetoothDevice.TRANSPORT_LE
        )
        return bluetoothGatt
    }

    private fun onGattReady(gatt: BluetoothGatt) {
        // GATT έτοιμο για λειτουργίες ανάγνωσης/εγγραφής
    }

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

Η κλάση GattConnector παρουσιάζει τη σωστή δημιουργία BluetoothGatt. Η παράμετρος autoConnect=false — άμεση σύνδεση (για σαρωμένες συσκευές). Στο onConnectionStateChange με STATE_CONNECTED καλείται αμέσως το discoverServices(). Το onServicesDiscovered σηματοδοτεί την ετοιμότητα GATT. Η close() καλεί διαδοχικά disconnect() και close() — χωρίς close() οι πόροι συστήματος δεν απελευθερώνονται, οδηγώντας σε διαρροή συνδέσεων BLE.

Ανακάλυψη υπηρεσιών και χαρακτηριστικών μέσω BluetoothGatt

discoverServices() — η πρώτη μέθοδος GATT που καλείται μετά τη σύνδεση. Ξεκινά ασύγχρονη αναζήτηση όλων των υπηρεσιών στην περιφέρεια BLE. Το αποτέλεσμα έρχεται στο onServicesDiscovered() με κωδικό κατάστασης: GATT_SUCCESS (0) — επιτυχία, 133 — GATT_ERROR, 8 — GATT_CONNECTION_TIMEOUT. Μετά από επιτυχή ανακάλυψη, το BluetoothGatt συμπληρώνει τη λίστα υπηρεσιών που είναι διαθέσιμες μέσω getServices().

Κάθε BluetoothGattService περιέχει μια λίστα BluetoothGattCharacteristic. Το χαρακτηριστικό έχει UUID, ιδιότητες (PROPERTY_READ, PROPERTY_WRITE, PROPERTY_NOTIFY) και προαιρετικούς περιγραφείς. Οι ιδιότητες καθορίζουν ποιες λειτουργίες επιτρέπονται: αν το χαρακτηριστικό δεν έχει PROPERTY_READ, η κλήση readCharacteristic θα επιστρέψει σφάλμα. Για λήψη των περιγραφέων του χαρακτηριστικού χρησιμοποιείται το getDescriptors().

kotlin
// Ανακάλυψη υπηρεσίας και αναζήτηση χαρακτηριστικού
class GattServiceExplorer {

    // Εύρεση υπηρεσίας κατά UUID
    fun findService(gatt: BluetoothGatt, uuid: UUID): BluetoothGattService? {
        return gatt.services?.firstOrNull { it.uuid == uuid }
    }

    // Εύρεση χαρακτηριστικού στην υπηρεσία
    fun findCharacteristic(
        service: BluetoothGattService,
        uuid: UUID
    ): BluetoothGattCharacteristic? {
        return service.characteristics?.firstOrNull { it.uuid == uuid }
    }

    // Λήψη όλων των υποστηριζόμενων λειτουργιών χαρακτηριστικού
    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
    }

    // Καταγραφή ολόκληρης ιεραρχίας GATT
    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}")
                }
            }
        }
    }
}

Η κλάση GattServiceExplorer παρέχει βοηθητικά προγράμματα για πλοήγηση στην ιεραρχία GATT. Οι findService και findCharacteristic αναζητούν υπηρεσίες και χαρακτηριστικά βάσει UUID. Η getCharacteristicProperties ελέγχει bitmasks ιδιοτήτων μέσω and(). Η dumpGattTree εξάγει ολόκληρη την ιεραρχία στο αρχείο καταγραφής — χρήσιμη κατά τον εντοπισμό σφαλμάτων συσκευών BLE. Όλες οι λειτουργίες στο BluetoothGatt πρέπει να εκτελούνται μετά από επιτυχημένο onServicesDiscovered, διαφορετικά η getServices() θα επιστρέψει κενή λίστα.

Ανάγνωση χαρακτηριστικών BLE: readCharacteristic και readDescriptor

readCharacteristic() — ασύγχρονη μέθοδος BluetoothGatt για ανάγνωση της τιμής χαρακτηριστικού από απομακρυσμένη συσκευή BLE. Το αποτέλεσμα έρχεται στο onCharacteristicRead() του BluetoothGattCallback. Εάν υπάρχουν 2+ αντίστοιχα χαρακτηριστικά στη συσκευή (απίθανο αλλά πιθανό), η readCharacteristic() μπορεί να διαβάσει λάθος στόχο — είναι ασφαλέστερο να καλείτε readCharacteristic() στο στιγμιότυπο BluetoothGattCharacteristic, όχι βάσει UUID.

readDescriptor() — μέθοδος για ανάγνωση της τιμής περιγραφέα χαρακτηριστικού. Ένας τυπικός περιγραφέας — CCCD (Client Characteristic Configuration Descriptor, UUID 0x2902) που καθορίζει εάν οι ειδοποιήσεις είναι ενεργοποιημένες. Αποτέλεσμα στο onDescriptorRead(). Η ανάγνωση περιγραφέων σπάνια απαιτείται στην πράξη — το CCCD διαχειρίζεται από το setCharacteristicNotification(), αλλά για προσαρμοσμένους περιγραφείς (User Description 0x2901, Presentation Format 0x2904) η readDescriptor() είναι ο μόνος τρόπος λήψης μεταδεδομένων.

MTU και ανάγνωση μεγάλων δεδομένων — εάν η τιμή χαρακτηριστικού υπερβαίνει το MTU (23 byte για BLE 4.0), το Android αυτόματα κατατμεί και συναρμολογεί δεδομένα μέσω μιας σειράς αιτημάτων ανάγνωσης μέσω της στοίβας BLE. Για BLE 5.0+ με εκτεταμένο MTU (έως 251 byte) δεν απαιτείται κατατμηματισμός — μία ανάγνωση επιστρέφει πλήρη δεδομένα. Πριν από την ανάγνωση μπορεί να κληθεί requestMtu() για διαπραγμάτευση μέγιστου MTU.

kotlin
// Ανάγνωση χαρακτηριστικού και περιγραφέα μέσω BluetoothGatt
class GattReader {

    fun readHeartRate(gatt: BluetoothGatt) {
        // UUID υπηρεσίας Heart Rate = 0x180D
        val service = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
            ?: return
        // UUID μέτρησης 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) }
    }

    // Επεξεργασία δεδομένων στην επανάκληση onCharacteristicRead:
    fun parseHeartRate(value: ByteArray): Int {
        // BLE Heart Rate: byte = flags, δεύτερο = 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
    }
}

Η κλάση GattReader παρουσιάζει την ανάγνωση του χαρακτηριστικού Heart Rate. Η υπηρεσία 0x180D περιέχει το χαρακτηριστικό 0x2A37 (Heart Rate Measurement) — το τυπικό προφίλ BLE του Bluetooth SIG. Πριν από την ανάγνωση, η ιδιότητα PROPERTY_READ ελέγχεται μέσω hasProperty. Η parseHeartRate αναλύει τη μορφή BLE καρδιακών παλμών: πρώτο byte — flags (μορφή δεδομένων), δεύτερο — τιμή bpm. Ο περιγραφέας CCCD (0x2902) διαβάζεται για έλεγχο της κατάστασης ειδοποιήσεων.

Εγγραφή χαρακτηριστικών: writeCharacteristic με WriteType

writeCharacteristic() — μέθοδος BluetoothGatt για εγγραφή δεδομένων στην περιφέρεια BLE. Στο Android API 33+, η writeCharacteristic() αντικαταστάθηκε από writeCharacteristic(request), όπου BluetoothGattCharacteristicWriteRequest είναι ένα αντικείμενο αιτήματος που περιέχει το χαρακτηριστικό, έναν πίνακα byte και WriteType. Η παλιά μέθοδος writeCharacteristic(characteristic) με setValue() είναι παρωχημένη. Το WriteType καθορίζει τη συμπεριφορά αιτήματος: WRITE_TYPE_DEFAULT (εξαρτάται από ιδιότητες χαρακτηριστικού), WRITE_TYPE_NO_RESPONSE (withoutResponse) και WRITE_TYPE_SIGNED (εξουσιοδότηση).

Η επιλογή WriteType επηρεάζει την ταχύτητα και αξιοπιστία. Το WRITE_TYPE_DEFAULT αντιστοιχεί συνήθως σε withResponse (εάν το χαρακτηριστικό έχει PROPERTY_WRITE) ή withoutResponse (εάν έχει PROPERTY_WRITE_NO_RESPONSE). Για δεδομένα ροής (ενημερώσεις OTA, αρχεία καταγραφής) χρησιμοποιήστε WRITE_TYPE_NO_RESPONSE — μέγιστο εύρος ζώνης. Για εντολές με εγγύηση παράδοσης (ενεργοποίηση, διαμόρφωση) — WRITE_TYPE_DEFAULT με επιβεβαίωση μέσω onCharacteristicWrite.

kotlin
// Εγγραφή χαρακτηριστικού BLE σε Android API 33+
class GattWriter {

    // Εγγραφή με απόκριση (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)
        }
    }

    // Εγγραφή χωρίς απόκριση (μέγιστη ταχύτητα)
    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 (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")
            }
        }
    }
}

Η κλάση GattWriter υποστηρίζει και τους δύο WriteType για διαφορετικά επίπεδα API. Η writeWithResponse χρησιμοποιεί WRITE_TYPE_DEFAULT — η συσκευή BLE επιβεβαιώνει την εγγραφή μέσω onCharacteristicWrite. Η writeWithoutResponse χρησιμοποιεί WRITE_TYPE_NO_RESPONSE — τα δεδομένα αποστέλλονται χωρίς επιβεβαίωση, μέγιστο εύρος ζώνης. Σε API 33+ χρησιμοποιείται η νέα writeCharacteristic(request) με BluetoothGattCharacteristicWriteRequest. Σε API < 33 — η παλιά setValue() + writeCharacteristic().

Εγγραφή σε ειδοποιήσεις: setCharacteristicNotification και CCCD

setCharacteristicNotification() — μέθοδος BluetoothGatt για εγγραφή σε ειδοποιήσεις αλλαγής χαρακτηριστικού στην περιφέρεια. Μετά την ενεργοποίηση της εγγραφής, η συσκευή BLE στέλνει νέες τιμές μέσω onCharacteristicChanged(). Ωστόσο, η setCharacteristicNotification() ενεργοποιεί μόνο την τοπική ειδοποίηση Android — για ενεργοποίηση ειδοποιήσεων στην ίδια τη συσκευή BLE, πρέπει επίσης να εγγραφεί η τιμή 0x0100 στον περιγραφέα CCCD (0x2902).

CCCD (Client Characteristic Configuration Descriptor) — περιγραφέας που διαχειρίζεται την αποστολή ειδοποιήσεων από την περιφέρεια BLE. Τιμή 0x0000 — ειδοποιήσεις απενεργοποιημένες, 0x0100 — ειδοποιήσεις ενεργοποιημένες (notifications), 0x0200 — ενδείξεις ενεργοποιημένες (indications). Η εγγραφή στο CCCD γίνεται μέσω writeDescriptor() στο BluetoothGatt μετά την κλήση setCharacteristicNotification(). Το Android δεν γράφει αυτόματα το CCCD — αυτή η ευθύνη ανήκει στον προγραμματιστή.

kotlin
// Σωστή εγγραφή σε ειδοποιήσεις BLE
class GattNotificationManager {

    // 1. Ενεργοποίηση ειδοποιήσεων
    fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
        // Βήμα 1: τοπική εγγραφή Android
        val success = gatt.setCharacteristicNotification(characteristic, true)
        if (!success) {
            print("Αποτυχία εγγραφής")
            return
        }

        // Βήμα 2: εγγραφή CCCD (0x2902) στη συσκευή BLE
        val cccdDescriptor = characteristic.getDescriptor(
            UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
        ) ?: return

        // 0x0100 = ειδοποίηση, 0x0200 = ένδειξη
        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. Ανακάλυψη χαρακτηριστικού
    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. Επανάκληση ειδοποίησης
    private val notificationCallback = object : BluetoothGattCallback() {
        override fun onCharacteristicChanged(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            value: ByteArray,
            callbackType: Int
        ) {
            // Νέα τιμή από περιφέρεια BLE
            print("Notification: ${value.size} bytes")
        }
    }
}

Η κλάση GattNotificationManager υλοποιεί το σωστό πρωτόκολλο δύο βημάτων για εγγραφή σε ειδοποιήσεις BLE. Η enableNotification πρώτα καλεί setCharacteristicNotification(true) στο Android, στη συνέχεια γράφει 0x0100 στον περιγραφέα CCCD μέσω writeDescriptor. Η disableNotification εκτελεί αντίστροφες λειτουργίες. Χωρίς εγγραφή CCCD, η συσκευή BLE δεν στέλνει ειδοποιήσεις — αυτό είναι το πιο συνηθισμένο λάθος προγραμματιστών BLE σε Android.

Παράδειγμα GATT-πελάτη σε Kotlin με BluetoothGattCallback

Πλήρες παράδειγμα GATT-πελάτη σε Kotlin, που συνδυάζει δημιουργία BluetoothGatt, ανακάλυψη, ανάγνωση και εγγραφή σε ειδοποιήσεις σε έναν ενιαίο διαχειριστή χρησιμοποιώντας coroutines για ασύγχρονη επεξεργασία.

kotlin
// Πλήρης GATT-πελάτης με coroutines σε Kotlin
class GattClient(context: Context) {

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

    // Σύνδεση με coroutine
    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
            )
        }

    // Ανάγνωση χαρακτηριστικού μέσω coroutine
    suspend fun readCharacteristicValue(char: BluetoothGattCharacteristic): ByteArray? =
        suspendCoroutine { continuation ->
            gatt?.let { gatt ->
                // Αποθήκευση ετικέτας χαρακτηριστικού για αναγνώριση επανάκλησης
                gatt.setCharacteristic(char, null)  // για API < 33
                gatt.readCharacteristic(char)
            }
        }

    // Κλείσιμο σύνδεσης
    fun release() {
        gatt?.disconnect()
        gatt?.close()
        gatt = null
    }
}

Ο GATT-πελάτης GattClient χρησιμοποιεί coroutines (suspendCoroutine) για να μετατρέψει το βασισμένο σε callback API BluetoothGatt σε ακολουθιακές κλήσεις. Η connect() περιμένει το onServicesDiscovered, μετά το οποίο η ιεραρχία GATT είναι διαθέσιμη. Η readCharacteristicValue() περιμένει το onCharacteristicRead. Αυτή η προσέγγιση εξαλείφει τα ένθετα callbacks και καθιστά τον κώδικα BLE γραμμικό. Η release() εγγυάται την απελευθέρωση πόρων — υποχρεωτική κλήση στο onDestroy του Activity ή ViewModel.onCleared.

Συχνές ερωτήσεις

Τι είναι το BluetoothGatt στο Android;

BluetoothGatt — κλάση για τον GATT-πελάτη Android που διαχειρίζεται σύνδεση BLE με περιφερειακή συσκευή. Δημιουργείται μέσω BluetoothDevice.connectGatt(), παρέχει μεθόδους discoverServices(), readCharacteristic(), writeCharacteristic(), setCharacteristicNotification(). Τα αποτελέσματα όλων των λειτουργιών έρχονται ασύγχρονα μέσω BluetoothGattCallback. Χωρίς BluetoothGatt, η αμφίδρομη επικοινωνία BLE σε Android είναι αδύνατη.

Γιατί το onServicesDiscovered επιστρέφει κατάσταση 133;

Η κατάσταση 133 (GATT_ERROR) σημαίνει εσωτερικό σφάλμα της στοίβας BLE Android. Αιτίες: η συσκευή αποσυνδέθηκε κατά την ανακάλυψη, το MTU είναι μικρότερο από το ελάχιστο (23 byte) ή η στοίβα BLE είναι υπερφορτωμένη. Λύση: επαναλάβετε το discoverServices() με καθυστέρηση 500 ms, ελέγξτε το RSSI της συσκευής και βεβαιωθείτε ότι η περιφέρεια υποστηρίζει ανακάλυψη GATT στην τρέχουσα κατάσταση.

Πώς να γράψω σωστά ένα χαρακτηριστικό με επιβεβαίωση;

Για εγγραφή με επιβεβαίωση, καλέστε writeCharacteristic() με WRITE_TYPE_DEFAULT (API 33+: BluetoothGattCharacteristicWriteRequest). Σε επιτυχία, η συσκευή BLE στέλνει επιβεβαίωση και το Android καλεί onCharacteristicWrite με GATT_SUCCESS. Εάν η συσκευή δεν αποκριθεί εντός 30 δευτερολέπτων (χρονικό όριο στοίβας), η επανάκληση επιστρέφει κατάσταση σφάλματος. Για watchdog, χρησιμοποιήστε Handler με postDelayed.

Πώς να εργαστώ με BLE σε Android 33+;

Σε Android 13+ (API 33) άλλαξαν οι μέθοδοι BluetoothGatt: η writeCharacteristic() τώρα δέχεται BluetoothGattCharacteristicWriteRequest, η readCharacteristic() — BluetoothGattCharacteristicReadRequest. Οι παλιές setValue()/writeCharacteristic() είναι παρωχημένες. Επίσης άλλαξε το BluetoothGattCallback: onCharacteristicRead(), onCharacteristicWrite(), onCharacteristicChanged() λαμβάνουν ByteArray value και callbackType. Χρησιμοποιήστε Build.VERSION.SDK_INT για διακλάδωση.

Πόσες συνδέσεις BLE υποστηρίζει το Android;

Το Android υποστηρίζει 4–8 ταυτόχρονες συνδέσεις BLE-GATT (εξαρτάται από τον κατασκευαστή και την έκδοση Android). Pixel/Google: έως 7, Samsung: έως 5, Xiaomi: έως 4. Κατά υπέρβαση του ορίου, η connectGatt επιστρέφει null ή το onConnectionStateChange καλείται με σφάλμα. Για εργασία με πολλές συσκευές, χρησιμοποιήστε κυκλική σύνδεση ή Bluetooth Mesh.

Σύνοψη

  • BluetoothGatt — GATT-πελάτης Android για σύνδεση BLE, δημιουργείται μέσω BluetoothDevice.connectGatt() με BluetoothGattCallback
  • discoverServices() — υποχρεωτικό βήμα μετά τη σύνδεση για λήψη υπηρεσιών, χαρακτηριστικών και περιγραφέων συσκευής BLE
  • Ανάγνωση — readCharacteristic() με ασύγχρονο αποτέλεσμα στο onCharacteristicRead()· για μεγάλα δεδομένα απαιτείται διαπραγμάτευση MTU
  • Εγγραφή — writeCharacteristic() με WriteType: DEFAULT (withResponse) ή NO_RESPONSE (χωρίς επιβεβαίωση)
  • Ειδοποιήσεις — ενεργοποίηση δύο βημάτων: setCharacteristicNotification() + εγγραφή CCCD (0x2902) με τιμή 0x0100
  • API 33+ — νέες μέθοδοι writeCharacteristic(request) και readCharacteristic(request) με αντικείμενα αιτημάτων
  • Απελευθέρωση πόρων — υποχρεωτική κλήση disconnect() και close() για αποφυγή διαρροής συνδέσεων BLE

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης