BluetoothGatt — kelas Android yang menyediakan API untuk kerja klien GATT (Generic Attribute Profile) melalui koneksi BLE. BluetoothGatt merangkum koneksi ke server GATT jarak jauh (perangkat BLE periferal) dan mengelola semua operasi profil: penemuan layanan, membaca dan menulis karakteristik, berlangganan notifikasi dan indikasi. Instance BluetoothGatt diperoleh melalui BluetoothDevice.connectGatt() dengan callback BluetoothGattCallback. Menurut Android Developers, 2026, BluetoothGatt adalah kelas pusat untuk komunikasi BLE dua arah, mendukung operasi GATT dari BLE 4.0 hingga BLE 5.4.
Poin utama
BluetoothGatt — adalah objek proxy yang mewakili koneksi GATT antara perangkat Android (pusat) dan periferal BLE (server). Setiap instance BluetoothGatt sesuai dengan satu koneksi BLE aktif. Melaluinya semua operasi profil GATT dilakukan: penemuan, pembacaan, penulisan, notifikasi. BluetoothGatt tidak dibuat langsung — dikembalikan oleh metode BluetoothDevice.connectGatt().
Pembuatan BluetoothGatt memerlukan empat parameter. Context — konteks aplikasi (Activity atau Application). autoConnect — jika false, Android segera memulai koneksi langsung; jika true, Android terhubung secara otomatis saat perangkat terdeteksi (berguna untuk koneksi latar belakang). BluetoothGattCallback — callback wajib untuk semua peristiwa GATT. transport — BluetoothDevice.TRANSPORT_LE (BLE) atau TRANSPORT_BREDR (Classic). Pada perangkat BLE selalu gunakan TRANSPORT_LE.
Siklus hidup BluetoothGatt terdiri dari lima keadaan. DISCONNECTED — keadaan awal. CONNECTING — setelah pemanggilan connectGatt, hingga konfirmasi. CONNECTED — setelah onConnectionStateChange dengan STATE_CONNECTED. Setelah terhubung, discoverServices() dipanggil untuk mendapatkan hierarki GATT. Setelah selesai bekerja — disconnect() dan close() untuk membebaskan sumber daya sistem. Tanpa pemanggilan close(), aplikasi dapat menghabiskan batas koneksi BLE Android (biasanya 4–8).
// Membuat koneksi BluetoothGatt
import android.bluetooth.*
class GattConnector(private val context: Context) {
private var bluetoothGatt: BluetoothGatt? = null
fun connect(device: BluetoothDevice): BluetoothGatt? {
// Tutup koneksi sebelumnya jika ada
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. Koneksi terbentuk → Penemuan
gatt.discoverServices()
}
BluetoothProfile.STATE_DISCONNECTED -> {
// 3. Koneksi terputus
close()
}
}
}
override fun onServicesDiscovered(
gatt: BluetoothGatt, status: Int
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 4. Hierarki GATT diterima
onGattReady(gatt)
}
}
},
BluetoothDevice.TRANSPORT_LE
)
return bluetoothGatt
}
private fun onGattReady(gatt: BluetoothGatt) {
// GATT siap untuk operasi baca/tulis
}
fun close() {
bluetoothGatt?.disconnect()
bluetoothGatt?.close()
bluetoothGatt = null
}
}
Kelas GattConnector mendemonstrasikan pembuatan BluetoothGatt yang benar. Parameter autoConnect=false — koneksi langsung (untuk perangkat yang dipindai). Pada onConnectionStateChange dengan STATE_CONNECTED, discoverServices() segera dipanggil. onServicesDiscovered menandakan kesiapan GATT. close() secara berurutan memanggil disconnect() dan close() — tanpa close() sumber daya sistem tidak dibebaskan, menyebabkan kebocoran koneksi BLE.
discoverServices() — metode GATT pertama yang dipanggil setelah koneksi. Memulai pencarian asinkron semua layanan pada periferal BLE. Hasilnya datang di onServicesDiscovered() dengan kode status: GATT_SUCCESS (0) — berhasil, 133 — GATT_ERROR, 8 — GATT_CONNECTION_TIMEOUT. Setelah penemuan berhasil, BluetoothGatt mengisi daftar layanan yang tersedia melalui getServices().
Setiap BluetoothGattService berisi daftar BluetoothGattCharacteristic. Karakteristik memiliki UUID, properti (PROPERTY_READ, PROPERTY_WRITE, PROPERTY_NOTIFY) dan deskriptor opsional. Properti menentukan operasi mana yang diizinkan: jika karakteristik tidak memiliki PROPERTY_READ, pemanggilan readCharacteristic akan mengembalikan kesalahan. Untuk mendapatkan deskriptor karakteristik, gunakan getDescriptors().
// Penemuan layanan dan pencarian karakteristik
class GattServiceExplorer {
// Temukan layanan berdasarkan UUID
fun findService(gatt: BluetoothGatt, uuid: UUID): BluetoothGattService? {
return gatt.services?.firstOrNull { it.uuid == uuid }
}
// Temukan karakteristik dalam layanan
fun findCharacteristic(
service: BluetoothGattService,
uuid: UUID
): BluetoothGattCharacteristic? {
return service.characteristics?.firstOrNull { it.uuid == uuid }
}
// Dapatkan semua operasi karakteristik yang didukung
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
}
// Catat seluruh hierarki 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}")
}
}
}
}
}
Kelas GattServiceExplorer menyediakan utilitas untuk navigasi hierarki GATT. findService dan findCharacteristic mencari layanan dan karakteristik berdasarkan UUID. getCharacteristicProperties memeriksa topeng bit properti melalui and(). dumpGattTree menampilkan hierarki lengkap ke log — berguna saat debugging perangkat BLE. Semua operasi pada BluetoothGatt harus dilakukan setelah onServicesDiscovered berhasil, jika tidak getServices() akan mengembalikan daftar kosong.
readCharacteristic() — metode asinkron BluetoothGatt untuk membaca nilai karakteristik dari perangkat BLE jarak jauh. Hasilnya datang di onCharacteristicRead() dari BluetoothGattCallback. Jika ada 2+ karakteristik yang cocok di perangkat (tidak mungkin tetapi mungkin), readCharacteristic() dapat membaca yang tidak ditargetkan — lebih aman memanggil readCharacteristic() pada instance BluetoothGattCharacteristic, bukan berdasarkan UUID.
readDescriptor() — metode untuk membaca nilai deskriptor karakteristik. Deskriptor tipikal — CCCD (Client Characteristic Configuration Descriptor, UUID 0x2902) yang menentukan apakah notifikasi diaktifkan. Hasil di onDescriptorRead(). Membaca deskriptor jarang diperlukan dalam praktik — CCCD dikelola oleh setCharacteristicNotification(), tetapi untuk deskriptor kustom (User Description 0x2901, Presentation Format 0x2904) readDescriptor() adalah satu-satunya cara mendapatkan metadata.
MTU dan membaca data besar — jika nilai karakteristik melebihi MTU (23 byte untuk BLE 4.0), Android secara otomatis memfragmentasi dan merakit data melalui urutan permintaan baca melalui tumpukan BLE. Untuk BLE 5.0+ dengan MTU yang diperluas (hingga 251 byte) fragmentasi tidak diperlukan — satu pembacaan mengembalikan data lengkap. Sebelum membaca, requestMtu() dapat dipanggil untuk menegosiasikan MTU maksimum.
// Membaca karakteristik dan deskriptor melalui BluetoothGatt
class GattReader {
fun readHeartRate(gatt: BluetoothGatt) {
// UUID Layanan Heart Rate = 0x180D
val service = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
?: return
// UUID Pengukuran 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) }
}
// Proses data di callback onCharacteristicRead:
fun parseHeartRate(value: ByteArray): Int {
// BLE Heart Rate: byte = flags, kedua = 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
}
}
Kelas GattReader mendemonstrasikan pembacaan karakteristik Heart Rate. Layanan 0x180D berisi karakteristik 0x2A37 (Heart Rate Measurement) — profil BLE standar Bluetooth SIG. Sebelum membaca, properti PROPERTY_READ diperiksa melalui hasProperty. parseHeartRate mem-parsing format BLE detak jantung: byte pertama — flags (format data), kedua — nilai bpm. Deskriptor CCCD (0x2902) dibaca untuk memeriksa status notifikasi.
writeCharacteristic() — metode BluetoothGatt untuk menulis data ke periferal BLE. Pada Android API 33+, writeCharacteristic() digantikan oleh writeCharacteristic(request), di mana BluetoothGattCharacteristicWriteRequest adalah objek permintaan yang berisi karakteristik, array byte, dan WriteType. Metode lama writeCharacteristic(characteristic) dengan setValue() sudah tidak digunakan lagi. WriteType menentukan perilaku permintaan: WRITE_TYPE_DEFAULT (tergantung properti karakteristik), WRITE_TYPE_NO_RESPONSE (withoutResponse) dan WRITE_TYPE_SIGNED (otorisasi).
Pemilihan WriteType mempengaruhi kecepatan dan keandalan. WRITE_TYPE_DEFAULT biasanya sesuai dengan withResponse (jika karakteristik memiliki PROPERTY_WRITE) atau withoutResponse (jika memiliki PROPERTY_WRITE_NO_RESPONSE). Untuk data aliran (pembaruan OTA, log) gunakan WRITE_TYPE_NO_RESPONSE — bandwidth maksimal. Untuk perintah dengan jaminan pengiriman (aktivasi, konfigurasi) — WRITE_TYPE_DEFAULT dengan konfirmasi melalui onCharacteristicWrite.
// Penulisan karakteristik BLE di Android API 33+
class GattWriter {
// Menulis dengan respons (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)
}
}
// Menulis tanpa respons (kecepatan maksimal)
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)
}
}
// Callback 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")
}
}
}
}
Kelas GattWriter mendukung kedua WriteType untuk tingkat API yang berbeda. writeWithResponse menggunakan WRITE_TYPE_DEFAULT — perangkat BLE mengonfirmasi penulisan melalui onCharacteristicWrite. writeWithoutResponse menggunakan WRITE_TYPE_NO_RESPONSE — data dikirim tanpa konfirmasi, bandwidth maksimal. Pada API 33+ digunakan writeCharacteristic(request) baru dengan BluetoothGattCharacteristicWriteRequest. Pada API < 33 — setValue() lama + writeCharacteristic().
setCharacteristicNotification() — metode BluetoothGatt untuk berlangganan notifikasi perubahan karakteristik pada periferal. Setelah aktivasi langganan, perangkat BLE mengirim nilai baru melalui onCharacteristicChanged(). Namun setCharacteristicNotification() hanya mengaktifkan notifikasi lokal Android — untuk mengaktifkan notifikasi pada perangkat BLE itu sendiri, perlu juga menulis nilai 0x0100 ke deskriptor CCCD (0x2902).
CCCD (Client Characteristic Configuration Descriptor) — deskriptor yang mengelola pengiriman notifikasi dari periferal BLE. Nilai 0x0000 — notifikasi dinonaktifkan, 0x0100 — notifikasi diaktifkan (notifications), 0x0200 — indikasi diaktifkan (indications). Penulisan ke CCCD dilakukan melalui writeDescriptor() pada BluetoothGatt setelah pemanggilan setCharacteristicNotification(). Android tidak menulis CCCD secara otomatis — tanggung jawab ini ada pada pengembang.
// Langganan notifikasi BLE yang benar
class GattNotificationManager {
// 1. Aktifkan notifikasi
fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
// Langkah 1: langganan lokal Android
val success = gatt.setCharacteristicNotification(characteristic, true)
if (!success) {
print("Gagal berlangganan")
return
}
// Langkah 2: tulis CCCD (0x2902) di perangkat BLE
val cccdDescriptor = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
) ?: return
// 0x0100 = notifikasi, 0x0200 = indikasi
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. Penemuan karakteristik
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. Callback notifikasi
private val notificationCallback = object : BluetoothGattCallback() {
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
callbackType: Int
) {
// Nilai baru dari periferal BLE
print("Notification: ${value.size} bytes")
}
}
}
Kelas GattNotificationManager mengimplementasikan protokol dua langkah yang benar untuk berlangganan notifikasi BLE. enableNotification pertama memanggil setCharacteristicNotification(true) pada Android, kemudian menulis 0x0100 ke deskriptor CCCD melalui writeDescriptor. disableNotification melakukan operasi sebaliknya. Tanpa penulisan CCCD, perangkat BLE tidak mengirim notifikasi — ini adalah kesalahan paling umum pengembang BLE di Android.
Contoh lengkap klien GATT di Kotlin, yang menggabungkan pembuatan BluetoothGatt, penemuan, pembacaan, dan berlangganan notifikasi dalam satu manajer menggunakan coroutine untuk pemrosesan asinkron.
// Klien GATT lengkap dengan coroutine di Kotlin
class GattClient(context: Context) {
private val context = context.applicationContext
private var gatt: BluetoothGatt? = null
// Hubungkan dengan 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
)
}
// Baca karakteristik melalui coroutine
suspend fun readCharacteristicValue(char: BluetoothGattCharacteristic): ByteArray? =
suspendCoroutine { continuation ->
gatt?.let { gatt ->
// Simpan tag karakteristik untuk identifikasi callback
gatt.setCharacteristic(char, null) // untuk API < 33
gatt.readCharacteristic(char)
}
}
// Tutup koneksi
fun release() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
Klien GATT GattClient menggunakan coroutine (suspendCoroutine) untuk mengubah API BluetoothGatt berbasis callback menjadi panggilan sekuensial. connect() menunggu onServicesDiscovered, setelah itu hierarki GATT tersedia. readCharacteristicValue() menunggu onCharacteristicRead. Pendekatan ini menghilangkan callback bersarang dan membuat kode BLE linier. release() menjamin pembebasan sumber daya — panggilan wajib di onDestroy Activity atau ViewModel.onCleared.
Pertanyaan yang sering diajukan
BluetoothGatt — kelas untuk klien GATT Android yang mengelola koneksi BLE dengan perangkat periferal. Dibuat melalui BluetoothDevice.connectGatt(), menyediakan metode discoverServices(), readCharacteristic(), writeCharacteristic(), setCharacteristicNotification(). Hasil semua operasi datang secara asinkron melalui BluetoothGattCallback. Tanpa BluetoothGatt, komunikasi BLE dua arah di Android tidak mungkin dilakukan.
Status 133 (GATT_ERROR) berarti kesalahan internal tumpukan BLE Android. Penyebab: perangkat terputus selama penemuan, MTU lebih kecil dari minimum (23 byte), atau tumpukan BLE kelebihan beban. Solusi: ulangi discoverServices() dengan penundaan 500 md, periksa RSSI perangkat, dan pastikan periferal mendukung penemuan GATT dalam keadaan saat ini.
Untuk menulis dengan konfirmasi, panggil writeCharacteristic() dengan WRITE_TYPE_DEFAULT (API 33+: BluetoothGattCharacteristicWriteRequest). Saat berhasil, perangkat BLE mengirim konfirmasi dan Android memanggil onCharacteristicWrite dengan GATT_SUCCESS. Jika perangkat tidak merespons dalam 30 detik (waktu habis tumpukan), callback mengembalikan status kesalahan. Untuk watchdog, gunakan Handler dengan postDelayed.
Di Android 13+ (API 33) metode BluetoothGatt berubah: writeCharacteristic() sekarang menerima BluetoothGattCharacteristicWriteRequest, readCharacteristic() — BluetoothGattCharacteristicReadRequest. Metode lama setValue()/writeCharacteristic() sudah tidak digunakan lagi. BluetoothGattCallback juga berubah: onCharacteristicRead(), onCharacteristicWrite(), onCharacteristicChanged() menerima ByteArray value dan callbackType. Gunakan Build.VERSION.SDK_INT untuk percabangan.
Android mendukung 4–8 koneksi BLE-GATT simultan (tergantung produsen dan versi Android). Pixel/Google: hingga 7, Samsung: hingga 5, Xiaomi: hingga 4. Saat melampaui batas, connectGatt mengembalikan null atau onConnectionStateChange dengan kesalahan. Untuk bekerja dengan banyak perangkat, gunakan koneksi siklik atau Bluetooth Mesh.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga