BluetoothGatt — BLE ulanishi ustida GATT-klienti (Generic Attribute Profile) ishlashi uchun API taqdim etuvchi Android sinfi. BluetoothGatt uzoqdagi GATT-serveriga (periferik BLE qurilmasiga) ulanishni inkapsulyatsiya qiladi va profilning barcha operatsiyalarini boshqaradi: xizmatlarni kashf qilish, xarakteristikalarni o'qish va yozish, bildirishnoma va indikatsiyalarga obuna bo'lish. BluetoothGatt namunasi BluetoothDevice.connectGatt() orqali BluetoothGattCallback bilan olinadi. Android Developers, 2026 ma'lumotlariga ko'ra, BluetoothGatt ikki tomonlama BLE aloqasi uchun markaziy sinf bo'lib, BLE 4.0 dan BLE 5.4 gacha GATT operatsiyalarini qo'llab-quvvatlaydi.
Asosiy ma'lumotlar
BluetoothGatt — Android qurilmasi (markaz) va BLE periferiyasi (server) o'rtasidagi GATT ulanishini ifodalovchi proksi-ob'ektdir. Har bir BluetoothGatt namunasi bitta faol BLE ulanishiga mos keladi. U orqali barcha GATT profil operatsiyalari bajariladi: kashf qilish, o'qish, yozish, bildirishnomalar. BluetoothGatt to'g'ridan-to'g'ri yaratilmaydi — u BluetoothDevice.connectGatt() metodi tomonidan qaytariladi.
BluetoothGatt yaratish to'rt parametrni talab qiladi. Context — ilova konteksti (Activity yoki Application). autoConnect — false bo'lsa, Android darhol to'g'ridan-to'g'ri ulanishni boshlaydi; true bo'lsa, Android qurilma aniqlanganda avtomatik ulanadi (fon ulanishi uchun foydali). BluetoothGattCallback — barcha GATT hodisalari uchun majburiy callback. transport — BluetoothDevice.TRANSPORT_LE (BLE) yoki TRANSPORT_BREDR (Classic). BLE qurilmalarida har doim TRANSPORT_LE dan foydalaning.
BluetoothGatt hayot aylanishi besh holatdan iborat. DISCONNECTED — boshlang'ich holat. CONNECTING — connectGatt chaqirilgandan so'ng, tasdiqlashgacha. CONNECTED — STATE_CONNECTED bilan onConnectionStateChange dan so'ng. Ulangandan so'ng GATT iyerarxiyasini olish uchun discoverServices() chaqiriladi. Ish tugagandan so'ng — tizim resurslarini bo'shatish uchun disconnect() va close(). close() chaqirilmasa, ilova Android BLE ulanish limitini tugatishi mumkin (odatda 4–8).
// BluetoothGatt ulanishini yaratish
import android.bluetooth.*
class GattConnector(private val context: Context) {
private var bluetoothGatt: BluetoothGatt? = null
fun connect(device: BluetoothDevice): BluetoothGatt? {
// Agar mavjud bo'lsa, oldingi ulanishni yop
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. Ulanish o'rnatildi → Kashf
gatt.discoverServices()
}
BluetoothProfile.STATE_DISCONNECTED -> {
// 3. Ulanish yo'qoldi
close()
}
}
}
override fun onServicesDiscovered(
gatt: BluetoothGatt, status: Int
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 4. GATT iyerarxiyasi olindi
onGattReady(gatt)
}
}
},
BluetoothDevice.TRANSPORT_LE
)
return bluetoothGatt
}
private fun onGattReady(gatt: BluetoothGatt) {
// GATT o'qish/yozish operatsiyalariga tayyor
}
fun close() {
bluetoothGatt?.disconnect()
bluetoothGatt?.close()
bluetoothGatt = null
}
}
GattConnector sinfi BluetoothGattni to'g'ri yaratishni namoyish etadi. autoConnect=false parametri — to'g'ridan-to'g'ri ulanish (skanerlangan qurilmalar uchun). STATE_CONNECTED bilan onConnectionStateChange da darhol discoverServices() chaqiriladi. onServicesDiscovered GATT tayyorligini bildiradi. close() ketma-ket disconnect() va close() ni chaqiradi — close() bo'lmasa, tizim resurslari bo'shamaydi, bu BLE ulanish oqishiga olib keladi.
discoverServices() — ulangandan so'ng chaqiriladigan birinchi GATT metodi. BLE periferiyasida barcha xizmatlarning asinxron qidiruvini boshlaydi. Natija onServicesDiscovered() da status kodi bilan keladi: GATT_SUCCESS (0) — muvaffaqiyatli, 133 — GATT_ERROR, 8 — GATT_CONNECTION_TIMEOUT. Muvaffaqiyatli kashfdan so'ng BluetoothGatt getServices() orqali mavjud bo'lgan xizmatlar ro'yxatini to'ldiradi.
Har bir BluetoothGattService BluetoothGattCharacteristic ro'yxatini o'z ichiga oladi. Xarakteristika UUID, xususiyatlar (PROPERTY_READ, PROPERTY_WRITE, PROPERTY_NOTIFY) va ixtiyoriy deskriptorlarga ega. Xususiyatlar qaysi operatsiyalarga ruxsat berilganligini aniqlaydi: agar xarakteristikada PROPERTY_READ bo'lmasa, readCharacteristic chaqiruvi xato qaytaradi. Xarakteristikaning deskriptorlarini olish uchun getDescriptors() ishlatiladi.
// Xizmat kashfi va xarakteristika qidiruvi
class GattServiceExplorer {
// UUID bo'yicha xizmatni top
fun findService(gatt: BluetoothGatt, uuid: UUID): BluetoothGattService? {
return gatt.services?.firstOrNull { it.uuid == uuid }
}
// Xizmatda xarakteristikani top
fun findCharacteristic(
service: BluetoothGattService,
uuid: UUID
): BluetoothGattCharacteristic? {
return service.characteristics?.firstOrNull { it.uuid == uuid }
}
// Barcha qo'llab-quvvatlanadigan xarakteristika operatsiyalarini olish
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
}
// Butun GATT iyerarxiyasini logga yozish
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 iyerarxiyasida navigatsiya uchun vositalar taqdim etadi. findService va findCharacteristic UUID bo'yicha xizmatlar va xarakteristikalarni qidiradi. getCharacteristicProperties and() orqali bit maskalarini tekshiradi. dumpGattTree to'liq iyerarxiyani logga chiqaradi — BLE qurilmalarini disk raskadrovka qilishda foydali. BluetoothGatt ustidagi barcha operatsiyalar muvaffaqiyatli onServicesDiscovered dan so'ng bajarilishi kerak, aks holda getServices() bo'sh ro'yxat qaytaradi.
readCharacteristic() — uzoqdagi BLE qurilmasidan xarakteristika qiymatini o'qish uchun BluetoothGattning asinxron metodi. Natija BluetoothGattCallback ning onCharacteristicRead() funksiyasida keladi. Qurilmada 2+ mos xarakteristika bo'lsa (kam ehtimol, lekin mumkin), readCharacteristic() nomaqsadni o'qishi mumkin — UUID bo'yicha emas, balki BluetoothGattCharacteristic namunasida readCharacteristic() chaqirish xavfsizroq.
readDescriptor() — xarakteristika deskriptorining qiymatini o'qish uchun metod. Oddiy deskriptor — bildirishnomalar yoqilgan yoki o'chirilganligini aniqlaydigan CCCD (Client Characteristic Configuration Descriptor, UUID 0x2902). Natija onDescriptorRead() da. Amalda deskriptorlarni o'qish kamdan-kam talab qilinadi — CCCD setCharacteristicNotification() tomonidan boshqariladi, lekin maxsus deskriptorlar uchun (User Description 0x2901, Presentation Format 0x2904) readDescriptor() metama'lumotlarni olishning yagona usulidir.
MTU va katta ma'lumotlarni o'qish — xarakteristika qiymati MTU dan (BLE 4.0 uchun 23 bayt) katta bo'lsa, Android avtomatik ravishda BLE stek orqali o'qish so'rovlari ketma-ketligi bilan ma'lumotlarni fragmentlaydi va yig'adi. BLE 5.0+ uchun kengaytirilgan MTU (251 baytgacha) bilan fragmentatsiya talab qilinmaydi — bitta o'qish to'liq ma'lumotni qaytaradi. O'qishdan oldin maksimal MTUni kelishish uchun requestMtu() chaqirilishi mumkin.
// BluetoothGatt orqali xarakteristika va deskriptorni o'qish
class GattReader {
fun readHeartRate(gatt: BluetoothGatt) {
// Heart Rate xizmat UUID = 0x180D
val service = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
?: return
// Heart Rate o'lchov 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) }
}
// Ma'lumotni onCharacteristicRead callbackida qayta ishlash:
fun parseHeartRate(value: ByteArray): Int {
// BLE Heart Rate: baytlar = flags, ikkinchi = 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 xarakteristikasini o'qishni namoyish etadi. 0x180D xizmati 0x2A37 (Heart Rate Measurement) xarakteristikasini o'z ichiga oladi — Bluetooth SIG ning standart BLE profili. O'qishdan oldin hasProperty orqali PROPERTY_READ xususiyati tekshiriladi. parseHeartRate BLE yurak urish formatini tahlil qiladi: birinchi bayt — flags (ma'lumot formati), ikkinchi — bpm qiymati. CCCD deskriptori (0x2902) bildirishnoma holatini tekshirish uchun o'qiladi.
writeCharacteristic() — BLE periferiyasiga ma'lumot yozish uchun BluetoothGatt metodi. Android API 33+ da writeCharacteristic() writeCharacteristic(request) bilan almashtirilgan, bu yerda BluetoothGattCharacteristicWriteRequest xarakteristika, bayt massivi va WriteType ni o'z ichiga olgan so'rov obyektidir. setValue() bilan eski writeCharacteristic(characteristic) metodi eskirgan. WriteType so'rovning xatti-harakatini aniqlaydi: WRITE_TYPE_DEFAULT (xarakteristika xususiyatlariga bog'liq), WRITE_TYPE_NO_RESPONSE (withoutResponse) va WRITE_TYPE_SIGNED (avtorizatsiya).
WriteType tanlovi tezlik va ishonchlilikka ta'sir qiladi. WRITE_TYPE_DEFAULT odatda withResponse (agar xarakteristikada PROPERTY_WRITE bo'lsa) yoki withoutResponse (agar PROPERTY_WRITE_NO_RESPONSE bo'lsa) ga mos keladi. Oqim ma'lumotlari uchun (OTA yangilanishlar, loglar) WRITE_TYPE_NO_RESPONSE dan foydalaning — maksimal o'tkazish qobiliyati. Yetkazib berish kafolati bo'lgan buyruqlar uchun (yoqish, sozlash) — onCharacteristicWrite orqali tasdiqlash bilan WRITE_TYPE_DEFAULT.
// Android API 33+ da BLE xarakteristika yozish
class GattWriter {
// Javob bilan yozish (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)
}
}
// Javobsiz yozish (maksimal tezlik)
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 turli API darajalari uchun ikkala WriteTypeni qo'llab-quvvatlaydi. writeWithResponse WRITE_TYPE_DEFAULT dan foydalanadi — BLE qurilmasi onCharacteristicWrite orqali yozishni tasdiqlaydi. writeWithoutResponse WRITE_TYPE_NO_RESPONSE dan foydalanadi — ma'lumotlar tasdiqlashsiz yuboriladi, maksimal o'tkazish qobiliyati. API 33+ da BluetoothGattCharacteristicWriteRequest bilan yangi writeCharacteristic(request) ishlatiladi. API < 33 da — eski setValue() + writeCharacteristic().
setCharacteristicNotification() — periferiyada xarakteristikaning o'zgarishi haqida bildirishnomalarga obuna bo'lish uchun BluetoothGatt metodi. Obuna faollashtirilgandan so'ng, BLE qurilmasi onCharacteristicChanged() orqali yangi qiymatlarni yuboradi. Biroq setCharacteristicNotification() faqat Androidning lokal bildirishnomasini faollashtiradi — BLE qurilmasining o'zida bildirishnomalarni yoqish uchun CCCD deskriptoriga (0x2902) 0x0100 qiymatini ham yozish kerak.
CCCD (Client Characteristic Configuration Descriptor) — BLE periferiyasidan bildirishnomalarni yuborishni boshqaruvchi deskriptor. Qiymat 0x0000 — bildirishnomalar o'chirilgan, 0x0100 — bildirishnomalar yoqilgan (notifications), 0x0200 — indikatsiyalar yoqilgan (indications). CCCDga yozish setCharacteristicNotification() chaqirilgandan so'ng BluetoothGatt da writeDescriptor() orqali amalga oshiriladi. Android CCCDni avtomatik yozmaydi — bu mas'uliyat dasturchining zimmasidadir.
// To'g'ri BLE bildirishnoma obunasi
class GattNotificationManager {
// 1. Bildirishnomalarni yoqish
fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
// 1-qadam: lokal Android obunasi
val success = gatt.setCharacteristicNotification(characteristic, true)
if (!success) {
print("Obuna bo'lish muvaffaqiyatsiz")
return
}
// 2-qadam: BLE qurilmasida CCCD (0x2902) yozish
val cccdDescriptor = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
) ?: return
// 0x0100 = bildirishnoma, 0x0200 = indikatsiya
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 kashfi
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. Bildirishnoma callbacki
private val notificationCallback = object : BluetoothGattCallback() {
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
callbackType: Int
) {
// BLE periferiyasidan yangi qiymat
print("Notification: ${value.size} bytes")
}
}
}
GattNotificationManager sinfi BLE bildirishnomalariga obuna bo'lish uchun to'g'ri ikki bosqichli protokolni amalga oshiradi. enableNotification avval Android da setCharacteristicNotification(true) ni chaqiradi, so'ng writeDescriptor orqali CCCD deskriptoriga 0x0100 yozadi. disableNotification teskari operatsiyalarni bajaradi. CCCD yozilmasa, BLE qurilmasi bildirishnoma yubormaydi — bu Android da BLE dasturchilarining eng keng tarqalgan xatosidir.
To'liq namuna — BluetoothGatt yaratish, kashf qilish, o'qish va bildirishnomalarga obunani asinxron qayta ishlash uchun korutinlardan foydalangan holda yagona menejerda birlashtiruvchi Kotlin GATT-klienti.
// Kotlin da korutinlar bilan to'liq GATT klienti
class GattClient(context: Context) {
private val context = context.applicationContext
private var gatt: BluetoothGatt? = null
// Korutin bilan ulanish
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 orqali xarakteristikani o'qish
suspend fun readCharacteristicValue(char: BluetoothGattCharacteristic): ByteArray? =
suspendCoroutine { continuation ->
gatt?.let { gatt ->
// Callback identifikatsiyasi uchun xarakteristika teglarini saqlash
gatt.setCharacteristic(char, null) // API uchun < 33
gatt.readCharacteristic(char)
}
}
// Ulanishni yopish
fun release() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
GattClient GATT-klienti callback-ga asoslangan BluetoothGatt API ni ketma-ket chaqiruvlarga aylantirish uchun korutinlardan (suspendCoroutine) foydalanadi. connect() onServicesDiscovered ni kutadi, shundan so'ng GATT iyerarxiyasi mavjud bo'ladi. readCharacteristicValue() onCharacteristicRead ni kutadi. Ushbu yondashuv callbacklarning joylashishini bartaraf qiladi va BLE kodini chiziqli qiladi. release() resurslarni bo'shatishni kafolatlaydi — Activity ning onDestroy yoki ViewModel.onCleared da majburiy chaqiruv.
Tez-tez beriladigan savollar
BluetoothGatt — periferik qurilma bilan BLE ulanishini boshqaruvchi Android GATT-klienti sinfi. BluetoothDevice.connectGatt() orqali yaratiladi, discoverServices(), readCharacteristic(), writeCharacteristic(), setCharacteristicNotification() metodlarini taqdim etadi. Barcha operatsiyalarning natijalari asinxron ravishda BluetoothGattCallback orqali keladi. BluetoothGattsiz Android da ikki tomonlama BLE aloqasi mumkin emas.
133 statusi (GATT_ERROR) Android BLE stekining ichki xatosini anglatadi. Sabablar: kashf qilish paytida qurilma uzilgan, MTU minimaldan (23 bayt) kichik yoki BLE stek haddan tashqari yuklangan. Yechim: discoverServices() ni 500 ms kechikish bilan takrorlang, qurilma RSSI sini tekshiring va periferiya joriy holatda GATT kashfini qo'llab-quvvatlashiga ishonch hosil qiling.
Tasdiqlash bilan yozish uchun writeCharacteristic() ni WRITE_TYPE_DEFAULT bilan chaqiring (API 33+: BluetoothGattCharacteristicWriteRequest). Muvaffaqiyat bo'lganda BLE qurilmasi tasdiq yuboradi va Android onCharacteristicWrite ni GATT_SUCCESS bilan chaqiradi. Agar qurilma 30 soniya ichida javob bermasa (stek taym-auti), callback xato statusini qaytaradi. Watchdog uchun postDelayed bilan Handler dan foydalaning.
Android 13+ (API 33) da BluetoothGatt metodlari o'zgartirilgan: writeCharacteristic() endi BluetoothGattCharacteristicWriteRequest ni qabul qiladi, readCharacteristic() — BluetoothGattCharacteristicReadRequest ni. Eski setValue()/writeCharacteristic() eskirgan. BluetoothGattCallback ham o'zgartirilgan: onCharacteristicRead(), onCharacteristicWrite(), onCharacteristicChanged() ByteArray value va callbackType ni oladi. Tarmoqlanish uchun Build.VERSION.SDK_INT dan foydalaning.
Android 4–8 bir vaqtning o'zida BLE-GATT ulanishini qo'llab-quvvatlaydi (ishlab chiqaruvchi va Android versiyasiga bog'liq). Pixel/Google: 7 gacha, Samsung: 5 gacha, Xiaomi: 4 gacha. Limit oshib ketganda connectGatt null qaytaradi yoki onConnectionStateChange xato bilan chaqiriladi. Ko'p sonli qurilmalar bilan ishlash uchun davriy ulanish yoki Bluetooth Mesh dan foydalaning.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.