BluetoothGatt — BLE bağlantısı üzərində GATT-klientinin (Generic Attribute Profile) işləməsi üçün API təmin edən Android sinfi. BluetoothGatt uzaq GATT-serverinə (periferik BLE cihazı) qoşulmanı kapsullaşdırır və profilin bütün əməliyyatlarını idarə edir: xidmətlərin kəşfi, xarakteristikaların oxunması və yazılması, bildirişlərə və indikasiyalara abunə olma. BluetoothGatt nümunəsi BluetoothDevice.connectGatt() vasitəsilə BluetoothGattCallback ilə alınır. Android Developers, 2026-ya görə, BluetoothGatt ikitərəfli BLE rabitəsi üçün mərkəzi sinifdir, BLE 4.0-dan BLE 5.4-ə qədər GATT əməliyyatlarını dəstəkləyir.
Əsas məqamlar
BluetoothGatt — Android cihazı (mərkəz) ilə BLE periferiyası (server) arasında GATT bağlantısını təmsil edən proksi-obyektdir. Hər BluetoothGatt nümunəsi bir aktiv BLE bağlantısına uyğun gəlir. Onun vasitəsilə bütün GATT profil əməliyyatları yerinə yetirilir: kəşf, oxu, yazma, bildirişlər. BluetoothGatt birbaşa yaradılmır — BluetoothDevice.connectGatt() metodu tərəfindən qaytarılır.
BluetoothGatt-ın yaradılması dörd parametr tələb edir. Context — tətbiq konteksti (Activity və ya Application). autoConnect — false olarsa, Android dərhal birbaşa qoşulmağa başlayır; true olarsa, Android cihaz aşkar edildikdə avtomatik qoşulur (fon bağlantısı üçün faydalıdır). BluetoothGattCallback — bütün GATT hadisələri üçün məcburi callback. transport — BluetoothDevice.TRANSPORT_LE (BLE) və ya TRANSPORT_BREDR (Classic). BLE cihazlarında həmişə TRANSPORT_LE istifadə edin.
BluetoothGatt həyat dövrü beş vəziyyətdən ibarətdir. DISCONNECTED — ilkin vəziyyət. CONNECTING — connectGatt çağırıldıqdan sonra, təsdiqə qədər. CONNECTED — STATE_CONNECTED ilə onConnectionStateChange-dən sonra. Qoşulduqdan sonra GATT iyerarxiyasını əldə etmək üçün discoverServices() çağırılır. İş bitdikdən sonra sistem resurslarını boşaltmaq üçün disconnect() və close(). close() çağırılmazsa, tətbiq Android BLE bağlantı limitini tükədə bilər (adətən 4–8).
// BluetoothGatt bağlantısının yaradılması
import android.bluetooth.*
class GattConnector(private val context: Context) {
private var bluetoothGatt: BluetoothGatt? = null
fun connect(device: BluetoothDevice): BluetoothGatt? {
// Əgər varsa, əvvəlki bağlantını bağla
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. Bağlantı quruldu → Kəşf
gatt.discoverServices()
}
BluetoothProfile.STATE_DISCONNECTED -> {
// 3. Bağlantı itirildi
close()
}
}
}
override fun onServicesDiscovered(
gatt: BluetoothGatt, status: Int
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 4. GATT iyerarxiyası alındı
onGattReady(gatt)
}
}
},
BluetoothDevice.TRANSPORT_LE
)
return bluetoothGatt
}
private fun onGattReady(gatt: BluetoothGatt) {
// GATT oxu/yazma əməliyyatlarına hazırdır
}
fun close() {
bluetoothGatt?.disconnect()
bluetoothGatt?.close()
bluetoothGatt = null
}
}
GattConnector sinfi BluetoothGatt-ın düzgün yaradılmasını nümayiş etdirir. autoConnect=false parametri — birbaşa qoşulma (skan edilmiş cihazlar üçün). STATE_CONNECTED ilə onConnectionStateChange-də dərhal discoverServices() çağırılır. onServicesDiscovered GATT-ın hazır olduğunu bildirir. close() ardıcıl olaraq disconnect() və close() çağırır — close() olmadan sistem resursları boşalmır, bu da BLE bağlantı sızmasına səbəb olur.
discoverServices() — qoşulduqdan sonra çağırılan ilk GATT metodu. BLE periferiyasında bütün xidmətlərin asinxron axtarışını başladır. Nəticə onServicesDiscovered() ilə status kodu ilə gəlir: GATT_SUCCESS (0) — uğurlu, 133 — GATT_ERROR, 8 — GATT_CONNECTION_TIMEOUT. Uğurlu kəşfdən sonra BluetoothGatt getServices() vasitəsilə əldə edilə bilən xidmətlər siyahısını doldurur.
Hər BluetoothGattService BluetoothGattCharacteristic siyahısını ehtiva edir. Xarakteristikanın UUID, xassələri (PROPERTY_READ, PROPERTY_WRITE, PROPERTY_NOTIFY) və isteğe bağlı deskriptorları var. Xassələr hansı əməliyyatların icazəli olduğunu müəyyən edir: əgər xarakteristikada PROPERTY_READ yoxdursa, readCharacteristic çağırışı xəta qaytaracaq. Xarakteristikanın deskriptorlarını əldə etmək üçün getDescriptors() istifadə olunur.
// Xidmət kəşfi və xarakteristika axtarışı
class GattServiceExplorer {
// UUID ilə xidməti tap
fun findService(gatt: BluetoothGatt, uuid: UUID): BluetoothGattService? {
return gatt.services?.firstOrNull { it.uuid == uuid }
}
// Xidmətdə xarakteristikanı tap
fun findCharacteristic(
service: BluetoothGattService,
uuid: UUID
): BluetoothGattCharacteristic? {
return service.characteristics?.firstOrNull { it.uuid == uuid }
}
// Bütün dəstəklənən xarakteristika əməliyyatlarını əldə et
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
}
// Bütün GATT iyerarxiyasını loga yaz
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 sinfi GATT iyerarxiyasında naviqasiya üçün alətlər təqdim edir. findService və findCharacteristic UUID ilə xidmətləri və xarakteristikaları axtarır. getCharacteristicProperties and() vasitəsilə bit maskalarını yoxlayır. dumpGattTree tam iyerarxiyanı loga çıxarır — BLE cihazlarının debug edilməsi zamanı faydalıdır. BluetoothGatt üzərində bütün əməliyyatlar uğurlu onServicesDiscovered-dən sonra yerinə yetirilməlidir, əks halda getServices() boş siyahı qaytaracaq.
readCharacteristic() — uzaq BLE cihazından xarakteristikanın dəyərini oxumaq üçün BluetoothGatt-ın asinxron metodu. Nəticə BluetoothGattCallback-in onCharacteristicRead() funksiyasında gəlir. Cihazda 2+ uyğun xarakteristika varsa (az ehtimal, lakin mümkündür), readCharacteristic() hədəf olmayanı oxuya bilər — UUID ilə deyil, BluetoothGattCharacteristic nümunəsində readCharacteristic() çağırmaq daha təhlükəsizdir.
readDescriptor() — xarakteristikanın deskriptorunun dəyərini oxumaq üçün metod. Tipik deskriptor — bildirişlərin aktiv olub-olmadığını müəyyən edən CCCD (Client Characteristic Configuration Descriptor, UUID 0x2902). Nəticə onDescriptorRead() funksiyasında. Praktikada deskriptorların oxunması nadir hallarda tələb olunur — CCCD setCharacteristicNotification() tərəfindən idarə olunur, lakin fərdi deskriptorlar üçün (User Description 0x2901, Presentation Format 0x2904) readDescriptor() metadata əldə etməyin yeganə yoludur.
MTU və böyük məlumatların oxunması — xarakteristikanın dəyəri MTU-dan (BLE 4.0 üçün 23 bayt) böyükdürsə, Android avtomatik olaraq BLE stack vasitəsilə ardıcıl oxu sorğuları ilə məlumatı fragmentləşdirir və yığır. BLE 5.0+ üçün genişləndirilmiş MTU (251 bayta qədər) ilə fragmentasiya tələb olunmur — bir oxu tam məlumatı qaytarır. Oxumadan əvvəl maksimum MTU-nu razılaşdırmaq üçün requestMtu() çağırıla bilər.
// BluetoothGatt vasitəsilə xarakteristikanın və deskriptorun oxunması
class GattReader {
fun readHeartRate(gatt: BluetoothGatt) {
// Heart Rate xidmət UUID = 0x180D
val service = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
?: return
// Heart Rate ölçmə 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) }
}
// Məlumatı onCharacteristicRead callbackində emal et:
fun parseHeartRate(value: ByteArray): Int {
// BLE Heart Rate: baytlar = flaglar, ikinci = 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 sinfi Heart Rate xarakteristikasının oxunmasını nümayiş etdirir. 0x180D xidməti 0x2A37 (Heart Rate Measurement) xarakteristikasını ehtiva edir — Bluetooth SIG-nin standart BLE profili. Oxumadan əvvəl hasProperty vasitəsilə PROPERTY_READ xassəsi yoxlanılır. parseHeartRate BLE ürək döyüntü formatını təhlil edir: ilk bayt — flaglar (məlumat formatı), ikinci — bpm dəyəri. CCCD deskriptoru (0x2902) bildiriş statusunu yoxlamaq üçün oxunur.
writeCharacteristic() — BLE periferiyasına məlumat yazmaq üçün BluetoothGatt metodu. Android API 33+-da writeCharacteristic() writeCharacteristic(request) ilə əvəz edilmişdir, burada BluetoothGattCharacteristicWriteRequest xarakteristika, bayt massivi və WriteType ehtiva edən sorğu obyektidir. setValue() ilə köhnə writeCharacteristic(characteristic) metodu deprecate edilmişdir. WriteType sorğunun davranışını müəyyən edir: WRITE_TYPE_DEFAULT (xarakteristikanın xassələrindən asılıdır), WRITE_TYPE_NO_RESPONSE (withoutResponse) və WRITE_TYPE_SIGNED (avtorizasiya).
WriteType seçimi sürətə və etibarlılığa təsir edir. WRITE_TYPE_DEFAULT adətən withResponse (əgər xarakteristikada PROPERTY_WRITE varsa) və ya withoutResponse (əgər PROPERTY_WRITE_NO_RESPONSE varsa) uyğun gəlir. Axın məlumatları üçün (OTA yeniləmələr, loglar) WRITE_TYPE_NO_RESPONSE istifadə edin — maksimum ötürmə qabiliyyəti. Çatdırılma zəmanəti olan əmrlər üçün (aktivləşdirmə, konfiqurasiya) — onCharacteristicWrite vasitəsilə təsdiqləmə ilə WRITE_TYPE_DEFAULT.
// Android API 33+-da BLE xarakteristika yazması
class GattWriter {
// Cavabla yazma (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)
}
}
// Cavabsız yazma (max sürət)
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 callbacki (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 sinfi müxtəlif API səviyyələri üçün hər iki WriteType-ı dəstəkləyir. writeWithResponse WRITE_TYPE_DEFAULT istifadə edir — BLE cihazı onCharacteristicWrite vasitəsilə yazmanı təsdiqləyir. writeWithoutResponse WRITE_TYPE_NO_RESPONSE istifadə edir — məlumatlar təsdiqləmədən göndərilir, maksimum ötürmə qabiliyyəti. API 33+-da BluetoothGattCharacteristicWriteRequest ilə yeni writeCharacteristic(request) istifadə olunur. API < 33-də — köhnə setValue() + writeCharacteristic().
setCharacteristicNotification() — periferiyada xarakteristikanın dəyişməsi barədə bildirişlərə abunə olmaq üçün BluetoothGatt metodu. Abunə aktivləşdirildikdən sonra BLE cihazı onCharacteristicChanged() vasitəsilə yeni dəyərlər göndərir. Lakin setCharacteristicNotification() yalnız Android-in lokal bildirişini aktivləşdirir — BLE cihazının özündə bildirişləri aktivləşdirmək üçün CCCD deskriptoruna (0x2902) 0x0100 dəyərini də yazmaq lazımdır.
CCCD (Client Characteristic Configuration Descriptor) — BLE periferiyasından bildirişlərin göndərilməsini idarə edən deskriptor. Dəyər 0x0000 — bildirişlər söndürülüb, 0x0100 — bildirişlər aktivdir (notifications), 0x0200 — indikasiyalar aktivdir (indications). CCCD-yə yazma setCharacteristicNotification() çağırıldıqdan sonra BluetoothGatt-da writeDescriptor() vasitəsilə yerinə yetirilir. Android CCCD-ni avtomatik yazmır — bu məsuliyyət tərtibatçının üzərindədir.
// Düzgün BLE bildiriş abunəsi
class GattNotificationManager {
// 1. Bildirişləri aktivləşdir
fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
// Addım 1: lokal Android abunəsi
val success = gatt.setCharacteristicNotification(characteristic, true)
if (!success) {
print("Abunə olmaq mümkün olmadı")
return
}
// Addım 2: BLE cihazında CCCD (0x2902) yaz
val cccdDescriptor = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
) ?: return
// 0x0100 = bildiriş, 0x0200 = indikasiya
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. Xarakteristika kəşfi
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. Bildiriş callbacki
private val notificationCallback = object : BluetoothGattCallback() {
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
callbackType: Int
) {
// BLE periferiyasından yeni dəyər
print("Notification: ${value.size} bytes")
}
}
}
GattNotificationManager sinfi BLE bildirişlərinə abunə üçün düzgün iki addımlı protokolu tətbiq edir. enableNotification əvvəlcə Android-də setCharacteristicNotification(true) çağırır, sonra writeDescriptor vasitəsilə CCCD deskriptoruna 0x0100 yazır. disableNotification əks əməliyyatları yerinə yetirir. CCCD yazılmazsa, BLE cihazı bildiriş göndərmir — bu Android-də BLE tərtibatçılarının ən çox yayılmış səhvidir.
Tam nümunə — BluetoothGatt yaradılması, kəşf, oxu və bildirişlərə abunəni asinxron emal üçün korutinlərdən istifadə edərək vahid menecerdə birləşdirən Kotlin GATT-klienti.
// Kotlin-də korutinlərlə tam GATT klienti
class GattClient(context: Context) {
private val context = context.applicationContext
private var gatt: BluetoothGatt? = null
// Korutinlə qoşul
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
)
}
// Korutin vasitəsilə xarakteristikanı oxu
suspend fun readCharacteristicValue(char: BluetoothGattCharacteristic): ByteArray? =
suspendCoroutine { continuation ->
gatt?.let { gatt ->
// Callback identifikasiyası üçün xarakteristika teqini saxla
gatt.setCharacteristic(char, null) // API üçün < 33
gatt.readCharacteristic(char)
}
}
// Bağlantını bağla
fun release() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
GattClient GATT-klienti callback-based BluetoothGatt API-ni ardıcıl çağırışlara çevirmək üçün korutinlərdən (suspendCoroutine) istifadə edir. connect() onServicesDiscovered-i gözləyir, bundan sonra GATT iyerarxiyası əlçatan olur. readCharacteristicValue() onCharacteristicRead-i gözləyir. Bu yanaşma callback-lərin iç-içə yerləşməsini aradan qaldırır və BLE kodunu xətti edir. release() resursların boşaldılmasını təmin edir — Activity-nin onDestroy və ya ViewModel.onCleared-də məcburi çağırış.
Tez-tez verilən suallar
BluetoothGatt — periferik cihazla BLE bağlantısını idarə edən Android GATT-klienti sinfi. BluetoothDevice.connectGatt() vasitəsilə yaradılır, discoverServices(), readCharacteristic(), writeCharacteristic(), setCharacteristicNotification() metodlarını təqdim edir. Bütün əməliyyatların nəticələri asinxron olaraq BluetoothGattCallback vasitəsilə gəlir. BluetoothGatt olmadan Android-də ikitərəfli BLE rabitəsi mümkün deyil.
133 statusu (GATT_ERROR) Android BLE stackinin daxili xətası deməkdir. Səbəblər: kəşf zamanı cihazın ayrılması, MTU-nun minimaldan (23 bayt) kiçik olması və ya BLE stackinin həddən artıq yüklənməsi. Həll yolu: discoverServices()-i 500 ms gecikmə ilə təkrarlayın, cihazın RSSI-sini yoxlayın və periferiyanın cari vəziyyətdə GATT kəşfini dəstəklədiyinə əmin olun.
Təsdiqləmə ilə yazmaq üçün writeCharacteristic() WRITE_TYPE_DEFAULT ilə çağırın (API 33+: BluetoothGattCharacteristicWriteRequest). Uğur olduqda BLE cihazı təsdiq göndərir və Android onCharacteristicWrite-i GATT_SUCCESS ilə çağırır. Cihaz 30 saniyə ərzində cavab verməzsə (stack timeout), callback xəta statusu qaytarır. Watchdog üçün postDelayed ilə Handler istifadə edin.
Android 13+-da (API 33) BluetoothGatt metodları dəyişdirilib: writeCharacteristic() indi BluetoothGattCharacteristicWriteRequest qəbul edir, readCharacteristic() — BluetoothGattCharacteristicReadRequest. Köhnə setValue()/writeCharacteristic() deprecate edilib. BluetoothGattCallback də dəyişdirilib: onCharacteristicRead(), onCharacteristicWrite(), onCharacteristicChanged() ByteArray value və callbackType alır. Budaqlanma üçün Build.VERSION.SDK_INT istifadə edin.
Android 4–8 eyni vaxtda BLE-GATT bağlantısını dəstəkləyir (istehsalçıdan və Android versiyasından asılıdır). Pixel/Google: 7-yə qədər, Samsung: 5-ə qədər, Xiaomi: 4-ə qədər. Limit aşıldıqda connectGatt null qaytarır və ya onConnectionStateChange xəta ilə çağırılır. Çox sayda cihazla işləmək üçün dövri qoşulma və ya Bluetooth Mesh istifadə edin.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun