BluetoothGatt — bir BLE bağlantısı üzerinden GATT (Generic Attribute Profile) istemci işlemleri için API sağlayan bir Android sınıfıdır. BluetoothGatt, uzak bir GATT sunucusuna (çevresel BLE cihazı) bağlantıyı kapsüller ve tüm profil işlemlerini yönetir: hizmet keşfi, özellik okuma ve yazma, bildirim ve göstergelere abone olma. Bir BluetoothGatt örneği, BluetoothDevice.connectGatt() ile BluetoothGattCallback aracılığıyla elde edilir. Android Developers, 2026'ye göre BluetoothGatt, BLE 4.0'dan BLE 5.4'e kadar GATT işlemlerini destekleyen çift yönlü BLE iletişimi için merkezi sınıftır.
Önemli Noktalar
BluetoothGatt — bir Android cihazı (merkez) ile BLE çevre birimi (sunucu) arasındaki GATT bağlantısını temsil eden bir proxy nesnesidir. Her BluetoothGatt örneği, bir aktif BLE bağlantısına karşılık gelir. Tüm GATT profil işlemleri bunun üzerinden gerçekleştirilir: keşif, okuma, yazma, bildirimler. BluetoothGatt doğrudan oluşturulmaz — BluetoothDevice.connectGatt() yöntemi tarafından döndürülür.
Bir BluetoothGatt oluşturmak dört parametre gerektirir. Context — uygulama bağlamı (Activity veya Application). autoConnect — false ise, Android hemen doğrudan bağlantı başlatır; true ise, Android cihaz algılandığında otomatik olarak bağlanır (arka plan bağlantısı için kullanışlıdır). BluetoothGattCallback — tüm GATT olayları için zorunlu geri çağrı. transport — BluetoothDevice.TRANSPORT_LE (BLE) veya TRANSPORT_BREDR (Classic). BLE cihazlarında her zaman TRANSPORT_LE kullanın.
BluetoothGatt yaşam döngüsü beş durumdan oluşur. DISCONNECTED — başlangıç durumu. CONNECTING — connectGatt çağrısından sonra, onaylamadan önce. CONNECTED — STATE_CONNECTED ile onConnectionStateChange'den sonra. Bağlantıdan sonra, GATT hiyerarşisini almak için discoverServices() çağrılır. İş tamamlandıktan sonra — sistem kaynaklarını serbest bırakmak için disconnect() ve close(). close() çağrılmazsa, uygulama Android BLE bağlantı sınırını (genellikle 4–8) tüketebilir.
// BluetoothGatt bağlantısı oluşturuluyor
import android.bluetooth.*
class GattConnector(private val context: Context) {
private var bluetoothGatt: BluetoothGatt? = null
fun connect(device: BluetoothDevice): BluetoothGatt? {
// Önceki bağlantı varsa kapat
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ı kuruldu → Keşif
gatt.discoverServices()
}
BluetoothProfile.STATE_DISCONNECTED -> {
// 3. Bağlantı kayboldu
close()
}
}
}
override fun onServicesDiscovered(
gatt: BluetoothGatt, status: Int
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 4. GATT hiyerarşisi alındı
onGattReady(gatt)
}
}
},
BluetoothDevice.TRANSPORT_LE
)
return bluetoothGatt
}
private fun onGattReady(gatt: BluetoothGatt) {
// GATT okuma/yazma işlemleri için hazır
}
fun close() {
bluetoothGatt?.disconnect()
bluetoothGatt?.close()
bluetoothGatt = null
}
}
GattConnector sınıfı, doğru BluetoothGatt oluşturmayı gösterir. autoConnect=false parametresi doğrudan bağlantı anlamına gelir (taranan cihazlar için). STATE_CONNECTED ile onConnectionStateChange'de hemen discoverServices() çağrılır. onServicesDiscovered, GATT'nin hazır olduğunu bildirir. close() sırayla disconnect() ve close() çağrısı yapar — close() olmadan sistem kaynakları serbest bırakılmaz ve bu da BLE bağlantı sızıntılarına yol açar.
discoverServices() — bağlantıdan sonra çağrılan ilk GATT yöntemidir. BLE çevre birimindeki tüm hizmetlerin eşzamansız aramasını başlatır. Sonuç, bir durum koduyla onServicesDiscovered() içinde gelir: GATT_SUCCESS (0) — başarılı, 133 — GATT_ERROR, 8 — GATT_CONNECTION_TIMEOUT. Başarılı keşiften sonra BluetoothGatt, getServices() aracılığıyla erişilebilir hizmetlerin listesini doldurur.
Her BluetoothGattService, BluetoothGattCharacteristic listesi içerir. Bir özelliğin UUID'si, özellikleri (PROPERTY_READ, PROPERTY_WRITE, PROPERTY_NOTIFY) ve isteğe bağlı tanımlayıcıları vardır. Özellikler, hangi işlemlere izin verildiğini belirler: bir özellik PROPERTY_READ'e sahip değilse, readCharacteristic çağrısı hata döndürür. Özellik tanımlayıcılarını almak için getDescriptors() kullanın.
// Hizmet keşfi ve özellik arama
class GattServiceExplorer {
// UUID ile hizmet bul
fun findService(gatt: BluetoothGatt, uuid: UUID): BluetoothGattService? {
return gatt.services?.firstOrNull { it.uuid == uuid }
}
// Hizmette özellik bul
fun findCharacteristic(
service: BluetoothGattService,
uuid: UUID
): BluetoothGattCharacteristic? {
return service.characteristics?.firstOrNull { it.uuid == uuid }
}
// Desteklenen tüm özellik işlemlerini al
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("YAZ")
if (and(BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0) props.add("YANITSIZ_YAZ")
if (and(BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) props.add("NOTIFY")
if (and(BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) props.add("INDICATE")
}
return props
}
// Tüm GATT hiyerarşisini günlüğe kaydet
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 sınıfı, GATT hiyerarşisinde gezinmek için yardımcı programlar sağlar. findService ve findCharacteristic, UUID'ye göre hizmet ve özellik arar. getCharacteristicProperties, and() aracılığıyla özelliklerin bit maskelerini kontrol eder. dumpGattTree, günlüğe tam hiyerarşiyi yazdırır — BLE cihazlarında hata ayıklarken kullanışlıdır. BluetoothGatt üzerindeki tüm işlemler, başarılı onServicesDiscovered'dan sonra gerçekleştirilmelidir, aksi takdirde getServices() boş bir liste döndürür.
readCharacteristic() — uzak bir BLE cihazından özellik değerini okumak için eşzamansız BluetoothGatt yöntemidir. Sonuç, BluetoothGattCallback'in onCharacteristicRead() yönteminde gelir. Bir cihazda 2+ eşleşen özellik varsa (olası değil ancak mümkün), readCharacteristic() hedef olmayanı okuyabilir — UUID yerine bir BluetoothGattCharacteristic örneğinde readCharacteristic() çağırmak daha güvenlidir.
readDescriptor() — bir özellik tanımlayıcısının değerini okumak için yöntemdir. Tipik bir tanımlayıcı, bildirimlerin etkin olup olmadığını belirleyen CCCD'dir (Client Characteristic Configuration Descriptor, UUID 0x2902). Sonuç onDescriptorRead() içindedir. Pratikte tanımlayıcıları okumak nadiren gereklidir — CCCD, setCharacteristicNotification() tarafından yönetilir, ancak özel tanımlayıcılar (User Description 0x2901, Presentation Format 0x2904) için readDescriptor() meta veri almanın tek yoludur.
MTU ve büyük veri okuma — özellik değeri MTU'yu (BLE 4.0 için 23 bayt) aşarsa, Android, BLE yığını üzerinden bir dizi okuma isteğiyle verileri otomatik olarak parçalar ve yeniden birleştirir. Genişletilmiş MTU'ya (251 bayta kadar) sahip BLE 5.0+ için parçalama gerekmez — tek bir okuma tam verileri döndürür. Okumadan önce, maksimum MTU'yu müzakere etmek için requestMtu() çağırabilirsiniz.
// BluetoothGatt ile özellik ve tanımlayıcı oku
class GattReader {
fun readHeartRate(gatt: BluetoothGatt) {
// Kalp atış hızı hizmeti UUID = 0x180D
val service = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
?: return
// Kalp atış hızı ö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) }
}
// onCharacteristicRead geri çağrısında verileri işle:
fun parseHeartRate(value: ByteArray): Int {
// BLE kalp atış hızı: bytes = flags, second = 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 sınıfı, kalp atış hızı özelliğini okumayı gösterir. Hizmet 0x180D, 0x2A37 (Heart Rate Measurement) özelliğini içerir — Bluetooth SIG'nin standart BLE profili. Okumadan önce, hasProperty aracılığıyla PROPERTY_READ özelliği kontrol edilir. parseHeartRate, BLE nabız formatını ayrıştırır: ilk bayt — flags (veri formatı), ikinci — bpm değeri. CCCD tanımlayıcısı (0x2902), bildirim durumunu kontrol etmek için okunur.
writeCharacteristic() — BLE çevre birimine veri yazmak için BluetoothGatt yöntemidir. Android API 33+'te, writeCharacteristic() yerini writeCharacteristic(request)'e bırakmıştır, burada BluetoothGattCharacteristicWriteRequest, özellik, bayt dizisi ve WriteType'ı içeren bir istek nesnesidir. setValue() ile eski writeCharacteristic(characteristic) yöntemi kullanımdan kaldırılmıştır. WriteType, istek davranışını belirler: WRITE_TYPE_DEFAULT (özellik özelliklerine bağlı), WRITE_TYPE_NO_RESPONSE (yanıtsız) ve WRITE_TYPE_SIGNED (yetkilendirme).
WriteType seçimi hız ve güvenilirliği etkiler. WRITE_TYPE_DEFAULT genellikle withResponse (özellik PROPERTY_WRITE'e sahipse) veya withoutResponse (PROPERTY_WRITE_NO_RESPONSE'a sahipse) karşılık gelir. Akış verileri (OTA güncellemeleri, günlükler) için WRITE_TYPE_NO_RESPONSE kullanın — maksimum verim. Teslimat garantili komutlar (etkinleştirme, yapılandırma) için — onCharacteristicWrite aracılığıyla onaylama ile WRITE_TYPE_DEFAULT.
// Android API 33+ üzerinde BLE özellik yazma
class GattWriter {
// Yanıtla yaz (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)
}
}
// Yanıtsız yaz (maksimum hız)
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 geri çağrısı (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 sınıfı, farklı API seviyeleri için her iki WriteType'ı da destekler. writeWithResponse, WRITE_TYPE_DEFAULT kullanır — BLE cihazı, onCharacteristicWrite aracılığıyla yazmayı onaylar. writeWithoutResponse, WRITE_TYPE_NO_RESPONSE kullanır — veriler onaysız gönderilir, maksimum verim. API 33+'te, BluetoothGattCharacteristicWriteRequest ile yeni writeCharacteristic(request) kullanılır. API < 33'te — eski setValue() + writeCharacteristic().
setCharacteristicNotification() — çevre birimindeki özellik değişiklikleriyle ilgili bildirimlere abone olmak için BluetoothGatt yöntemidir. Abonelik etkinleştirildikten sonra, BLE cihazı onCharacteristicChanged() aracılığıyla yeni değerler gönderir. Ancak setCharacteristicNotification() yalnızca yerel Android bildirimini etkinleştirir — BLE cihazının kendisinde bildirimleri etkinleştirmek için CCCD tanımlayıcısına (0x2902) 0x0100 değerini de yazmalısınız.
CCCD (Client Characteristic Configuration Descriptor) — BLE çevre biriminden bildirim gönderimini kontrol eden tanımlayıcıdır. Değer 0x0000 — bildirimler devre dışı, 0x0100 — bildirimler etkin, 0x0200 — göstergeler etkin. CCCD'ye yazma, setCharacteristicNotification() çağrısından sonra BluetoothGatt üzerinde writeDescriptor() aracılığıyla yapılır. Android CCCD'yi otomatik olarak yazmaz — bu sorumluluk geliştiriciye aittir.
// Doğru BLE bildirim aboneliği
class GattNotificationManager {
// 1. Bildirimleri etkinleştir
fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
// Adım 1: yerel Android aboneliği
val success = gatt.setCharacteristicNotification(characteristic, true)
if (!success) {
print("Abone olunamadı")
return
}
// Adım 2: BLE cihazında CCCD (0x2902) yaz
val cccdDescriptor = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
) ?: return
// 0x0100 = bildirim, 0x0200 = gösterge
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. Özellik keş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. Bildirim geri çağrısı
private val notificationCallback = object : BluetoothGattCallback() {
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
callbackType: Int
) {
// BLE çevre biriminden yeni değer
print("Notification: ${value.size} bytes")
}
}
}
GattNotificationManager sınıfı, doğru iki adımlı BLE bildirim abonelik protokolünü uygular. enableNotification önce Android'de setCharacteristicNotification(true) çağırır, ardından writeDescriptor aracılığıyla CCCD tanımlayıcısına 0x0100 yazar. disableNotification ters işlemleri gerçekleştirir. CCCD yazılmazsa, BLE cihazı bildirim göndermez — bu, Android'deki BLE geliştiricilerinin en yaygın hatasıdır.
Tam örnek — eşzamansız işleme için coroutine kullanarak BluetoothGatt oluşturma, keşif, okuma ve bildirim aboneliğini tek bir yöneticide birleştiren Kotlin'de bir GATT istemcisi.
// Kotlin'de coroutine ile tam GATT istemcisi
class GattClient(context: Context) {
private val context = context.applicationContext
private var gatt: BluetoothGatt? = null
// Coroutine ile bağlan
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 aracılığıyla özellik oku
suspend fun readCharacteristicValue(char: BluetoothGattCharacteristic): ByteArray? =
suspendCoroutine { continuation ->
gatt?.let { gatt ->
// Geri çağrı tanımlaması için özellik etiketini kaydet
gatt.setCharacteristic(char, null) // API için < 33
gatt.readCharacteristic(char)
}
}
// Bağlantıyı kapat
fun release() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
GATT istemcisi GattClient, geri çağrı tabanlı BluetoothGatt API'sini sıralı çağrılara dönüştürmek için coroutine (suspendCoroutine) kullanır. connect(), onServicesDiscovered'ı bekler, ardından GATT hiyerarşisi kullanılabilir. readCharacteristicValue(), onCharacteristicRead'i bekler. Bu yaklaşım, geri çağrı iç içe geçmesini ortadan kaldırır ve BLE kodunu doğrusal hale getirir. release(), kaynak temizliğini garanti eder — Activity'nin onDestroy veya ViewModel.onCleared yönteminde zorunlu çağrı.
Sıkça Sorulan Sorular
BluetoothGatt — çevresel bir cihazla BLE bağlantısını yöneten GATT istemcisi için Android sınıfıdır. BluetoothDevice.connectGatt() aracılığıyla oluşturulur ve discoverServices(), readCharacteristic(), writeCharacteristic(), setCharacteristicNotification() yöntemlerini sağlar. Tüm işlemlerin sonuçları BluetoothGattCallback aracılığıyla eşzamansız olarak gelir. BluetoothGatt olmadan, Android'de çift yönlü BLE iletişimi imkansızdır.
Durum 133 (GATT_ERROR), Android BLE yığınında dahili bir hata olduğunu gösterir. Nedenleri: keşif sırasında cihazın bağlantısı kesildi, MTU minimumun (23 bayt) altında veya BLE yığını aşırı yüklendi. Çözüm: 500 ms gecikmeyle discoverServices() işlemini yeniden deneyin, cihaz RSSI'sini kontrol edin ve çevre biriminin mevcut durumunda GATT keşfini desteklediğinden emin olun.
Onayla yazmak için, WRITE_TYPE_DEFAULT (API 33+: BluetoothGattCharacteristicWriteRequest) ile writeCharacteristic() çağırın. Başarı durumunda, BLE cihazı bir onay gönderir ve Android, GATT_SUCCESS ile onCharacteristicWrite çağırır. Cihaz 30 saniye içinde (yığın zaman aşımı) yanıt vermezse, geri çağrı bir hata durumu döndürür. Watchdog için postDelayed ile Handler kullanın.
Android 13+ (API 33) üzerinde BluetoothGatt yöntemleri değişti: writeCharacteristic() artık BluetoothGattCharacteristicWriteRequest'i, readCharacteristic() ise BluetoothGattCharacteristicReadRequest'i kabul ediyor. Eski setValue()/writeCharacteristic() kullanımdan kaldırıldı. BluetoothGattCallback da değişti: onCharacteristicRead(), onCharacteristicWrite(), onCharacteristicChanged() ByteArray değeri ve callbackType alır. Dallandırma için Build.VERSION.SDK_INT kullanın.
Android, 4–8 eşzamanlı BLE GATT bağlantısını destekler (üreticiye ve Android sürümüne göre değişir). Pixel/Google: 7'ye kadar, Samsung: 5'e kadar, Xiaomi: 4'e kadar. Sınır aşıldığında, connectGatt null veya onConnectionStateChange hata ile döner. Çok sayıda cihazla çalışmak için döngüsel bağlantı veya Bluetooth Mesh kullanın.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun