Langkah pertama dalam berinteraksi dengan perangkat BLE adalah terhubung ke perangkat tersebut. Lebih khususnya, menghubungkan ke server GATT di perangkat. Untuk terhubung ke server GATT pada perangkat BLE, gunakan metode connectGatt()
. Metode ini memerlukan tiga parameter: objek Context
, autoConnect
(boolean
yang menunjukkan apakah akan otomatis terhubung ke perangkat BLE segera setelah
tersedia), dan referensi ke
BluetoothGattCallback
:
Kotlin
var bluetoothGatt: BluetoothGatt? = null ... bluetoothGatt = device.connectGatt(this, false, gattCallback)
Java
bluetoothGatt = device.connectGatt(this, false, gattCallback);
Tindakan ini terhubung ke server GATT yang dihosting oleh perangkat BLE, dan menampilkan instance BluetoothGatt
, yang kemudian dapat Anda gunakan untuk melakukan operasi klien GATT. Pemanggil (aplikasi Android)
adalah klien GATT. BluetoothGattCallback
digunakan untuk memberikan hasil kepada klien, seperti status koneksi, serta operasi klien GATT lebih lanjut.
Menyiapkan layanan terikat
Pada contoh berikut, aplikasi BLE menyediakan aktivitas
(DeviceControlActivity
) untuk terhubung ke perangkat Bluetooth, menampilkan data perangkat,
dan menampilkan layanan dan karakteristik GATT yang didukung oleh perangkat. Berdasarkan
input pengguna, aktivitas ini berkomunikasi dengan
Service
yang disebut BluetoothLeService
, yang
berinteraksi dengan perangkat BLE melalui BLE API. Komunikasi
dilakukan menggunakan layanan terikat yang memungkinkan
aktivitas terhubung ke BluetoothLeService
dan memanggil fungsi untuk
terhubung ke perangkat. BluetoothLeService
memerlukan
implementasi Binder
yang memberikan akses ke
layanan untuk aktivitas.
Kotlin
class BluetoothLeService : Service() { private val binder = LocalBinder() override fun onBind(intent: Intent): IBinder? { return binder } inner class LocalBinder : Binder() { fun getService() : BluetoothLeService { return this@BluetoothLeService } } }
Java
class BluetoothLeService extends Service { private Binder binder = new LocalBinder(); @Nullable @Override public IBinder onBind(Intent intent) { return binder; } class LocalBinder extends Binder { public BluetoothLeService getService() { return BluetoothLeService.this; } } }
Aktivitas tersebut dapat memulai layanan menggunakan
bindService()
,
dengan meneruskan Intent
untuk memulai
layanan, implementasi ServiceConnection
untuk memproses peristiwa koneksi dan pemutusan koneksi, serta flag
untuk menentukan opsi koneksi tambahan.
Kotlin
class DeviceControlActivity : AppCompatActivity() { private var bluetoothService : BluetoothLeService? = null // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected( componentName: ComponentName, service: IBinder ) { bluetoothService = (service as LocalBinder).getService() bluetoothService?.let { bluetooth -> // call functions on service to check connection and connect to devices } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.gatt_services_characteristics) val gattServiceIntent = Intent(this, BluetoothLeService::class.java) bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE) } }
Java
class DeviceControlActivity extends AppCompatActivity { private BluetoothLeService bluetoothService; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { // call functions on service to check connection and connect to devices } } @Override public void onServiceDisconnected(ComponentName name) { bluetoothService = null; } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.gatt_services_characteristics); Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); } }
Menyiapkan BluetoothAdapter
Setelah terikat, layanan perlu mengakses
BluetoothAdapter
. Tindakan ini akan
memeriksa apakah adaptor tersedia di perangkat. Baca Menyiapkan
Bluetooth untuk mengetahui informasi selengkapnya tentang
BluetoothAdapter
. Contoh berikut menggabungkan kode penyiapan ini dalam
fungsi initialize()
yang menampilkan nilai Boolean
yang menunjukkan keberhasilan.
Kotlin
private const val TAG = "BluetoothLeService" class BluetoothLeService : Service() { private var bluetoothAdapter: BluetoothAdapter? = null fun initialize(): Boolean { bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() if (bluetoothAdapter == null) { Log.e(TAG, "Unable to obtain a BluetoothAdapter.") return false } return true } ... }
Java
class BluetoothLeService extends Service { public static final String TAG = "BluetoothLeService"; private BluetoothAdapter bluetoothAdapter; public boolean initialize() { bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { Log.e(TAG, "Unable to obtain a BluetoothAdapter."); return false; } return true; } ... }
Aktivitas ini memanggil fungsi ini dalam implementasi ServiceConnection
-nya.
Penanganan nilai hasil yang salah dari fungsi initialize()
bergantung pada
aplikasi Anda. Anda dapat menampilkan pesan error kepada pengguna yang menunjukkan bahwa
perangkat saat ini tidak mendukung operasi Bluetooth atau menonaktifkan fitur apa pun
yang memerlukan Bluetooth agar dapat berfungsi. Pada contoh berikut,
finish()
dipanggil pada aktivitas
untuk mengirim pengguna kembali ke layar sebelumnya.
Kotlin
class DeviceControlActivity : AppCompatActivity() { // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected( componentName: ComponentName, service: IBinder ) { bluetoothService = (service as LocalBinder).getService() bluetoothService?.let { bluetooth -> if (!bluetooth.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth") finish() } // perform device connection } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } } ... }
Java
class DeviceControlsActivity extends AppCompatActivity { private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { if (!bluetoothService.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); finish(); } // perform device connection } } @Override public void onServiceDisconnected(ComponentName name) { bluetoothService = null; } }; ... }
Hubungkan ke perangkat
Setelah instance BluetoothLeService
diinisialisasi, instance tersebut dapat terhubung ke perangkat
BLE. Aktivitas perlu mengirim alamat perangkat ke layanan agar dapat
memulai koneksi. Layanan terlebih dahulu akan memanggil
getRemoteDevice()
di BluetoothAdapter
untuk mengakses perangkat. Jika adaptor tidak dapat menemukan
perangkat dengan alamat tersebut, getRemoteDevice()
akan menampilkan
IllegalArgumentException
.
Kotlin
fun connect(address: String): Boolean { bluetoothAdapter?.let { adapter -> try { val device = adapter.getRemoteDevice(address) } catch (exception: IllegalArgumentException) { Log.w(TAG, "Device not found with provided address.") return false } // connect to the GATT server on the device } ?: run { Log.w(TAG, "BluetoothAdapter not initialized") return false } }
Java
public boolean connect(final String address) { if (bluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; } try { final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); } catch (IllegalArgumentException exception) { Log.w(TAG, "Device not found with provided address."); return false; } // connect to the GATT server on the device }
DeviceControlActivity
memanggil fungsi connect()
ini setelah layanan
diinisialisasi. Aktivitas ini perlu meneruskan alamat perangkat BLE. Pada
contoh berikut, alamat perangkat diteruskan ke aktivitas sebagai tambahan
intent.
Kotlin
// Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected( componentName: ComponentName, service: IBinder ) { bluetoothService = (service as LocalBinder).getService() bluetoothService?.let { bluetooth -> if (!bluetooth.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth") finish() } // perform device connection bluetooth.connect(deviceAddress) } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } }
Java
private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { if (!bluetoothService.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); finish(); } // perform device connection bluetoothService.connect(deviceAddress); } } @Override public void onServiceDisconnected(ComponentName name) { bluetoothService = null; } };
Mendeklarasikan callback GATT
Setelah aktivitas memberi tahu layanan perangkat mana yang harus dihubungkan dan layanan
terhubung ke perangkat, layanan harus terhubung ke server GATT di
perangkat BLE. Koneksi ini memerlukan BluetoothGattCallback
untuk menerima
notifikasi tentang status koneksi, penemuan layanan, pembacaan karakteristik, dan notifikasi karakteristik.
Topik ini berfokus pada notifikasi status koneksi. Lihat Mentransfer data BLE untuk mengetahui cara melakukan penemuan layanan, pembacaan karakteristik, dan notifikasi karakteristik permintaan.
Fungsi onConnectionStateChange()
dipicu saat koneksi ke server GATT perangkat berubah.
Pada contoh berikut, callback ditentukan di class Service
sehingga
dapat digunakan dengan
BluetoothDevice
setelah
layanan terhubung.
Kotlin
private val bluetoothGattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { // successfully connected to the GATT Server } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // disconnected from the GATT Server } } }
Java
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { // successfully connected to the GATT Server } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // disconnected from the GATT Server } } };
Menghubungkan ke layanan GATT
Setelah BluetoothGattCallback
dideklarasikan, layanan dapat menggunakan
objek BluetoothDevice
dari fungsi connect()
untuk terhubung ke layanan GATT
di perangkat.
Fungsi
connectGatt()
digunakan. Hal ini memerlukan objek Context
, flag boolean autoConnect
, dan BluetoothGattCallback
. Dalam contoh ini, aplikasi terhubung
langsung ke perangkat BLE sehingga false
diteruskan untuk autoConnect
.
Properti BluetoothGatt
juga ditambahkan. Hal ini memungkinkan layanan menutup
koneksi saat tidak
diperlukan lagi.
Kotlin
class BluetoothLeService : Service() { ... private var bluetoothGatt: BluetoothGatt? = null ... fun connect(address: String): Boolean { bluetoothAdapter?.let { adapter -> try { val device = adapter.getRemoteDevice(address) // connect to the GATT server on the device bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback) return true } catch (exception: IllegalArgumentException) { Log.w(TAG, "Device not found with provided address. Unable to connect.") return false } } ?: run { Log.w(TAG, "BluetoothAdapter not initialized") return false } } }
Java
class BluetoothLeService extends Service { ... private BluetoothGatt bluetoothGatt; ... public boolean connect(final String address) { if (bluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; } try { final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); // connect to the GATT server on the device bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback); return true; } catch (IllegalArgumentException exception) { Log.w(TAG, "Device not found with provided address. Unable to connect."); return false; } } }
Update siaran
Saat terhubung atau terputus dari server GATT, server perlu memberi tahu aktivitas status baru. Ada beberapa cara untuk melakukannya. Contoh berikut menggunakan siaran untuk mengirim informasi dari layanan ke aktivitas.
Layanan mendeklarasikan fungsi untuk menyiarkan status baru. Fungsi ini mengambil
string tindakan yang diteruskan ke objek Intent
sebelum disiarkan
ke sistem.
Kotlin
private fun broadcastUpdate(action: String) { val intent = Intent(action) sendBroadcast(intent) }
Java
private void broadcastUpdate(final String action) { final Intent intent = new Intent(action); sendBroadcast(intent); }
Setelah diterapkan, fungsi siaran akan digunakan dalam BluetoothGattCallback
untuk mengirim informasi tentang status koneksi dengan server GATT. Konstanta dan status koneksi layanan saat ini dideklarasikan
dalam layanan yang mewakili tindakan Intent
.
Kotlin
class BluetoothLeService : Service() { private var connectionState = STATE_DISCONNECTED private val bluetoothGattCallback: BluetoothGattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { // successfully connected to the GATT Server connectionState = STATE_CONNECTED broadcastUpdate(ACTION_GATT_CONNECTED) } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // disconnected from the GATT Server connectionState = STATE_DISCONNECTED broadcastUpdate(ACTION_GATT_DISCONNECTED) } } } ... companion object { const val ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED" const val ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED" private const val STATE_DISCONNECTED = 0 private const val STATE_CONNECTED = 2 } }
Java
class BluetoothLeService extends Service { public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; private static final int STATE_DISCONNECTED = 0; private static final int STATE_CONNECTED = 2; private int connectionState; ... private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { // successfully connected to the GATT Server connectionState = STATE_CONNECTED; broadcastUpdate(ACTION_GATT_CONNECTED); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // disconnected from the GATT Server connectionState = STATE_DISCONNECTED; broadcastUpdate(ACTION_GATT_DISCONNECTED); } } }; … }
Memproses pembaruan dalam aktivitas
Setelah layanan menyiarkan update koneksi, aktivitas perlu
mengimplementasikan BroadcastReceiver
.
Daftarkan penerima ini saat menyiapkan aktivitas, dan batalkan pendaftarannya saat
aktivitas keluar dari layar. Dengan memproses peristiwa dari layanan,
aktivitas ini dapat mengupdate antarmuka pengguna berdasarkan status koneksi
saat ini dengan perangkat BLE.
Kotlin
class DeviceControlActivity : AppCompatActivity() { ... private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.action) { BluetoothLeService.ACTION_GATT_CONNECTED -> { connected = true updateConnectionState(R.string.connected) } BluetoothLeService.ACTION_GATT_DISCONNECTED -> { connected = false updateConnectionState(R.string.disconnected) } } } } override fun onResume() { super.onResume() registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()) if (bluetoothService != null) { val result = bluetoothService!!.connect(deviceAddress) Log.d(DeviceControlsActivity.TAG, "Connect request result=$result") } } override fun onPause() { super.onPause() unregisterReceiver(gattUpdateReceiver) } private fun makeGattUpdateIntentFilter(): IntentFilter? { return IntentFilter().apply { addAction(BluetoothLeService.ACTION_GATT_CONNECTED) addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED) } } }
Java
class DeviceControlsActivity extends AppCompatActivity { ... private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { connected = true; updateConnectionState(R.string.connected); } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { connected = false; updateConnectionState(R.string.disconnected); } } }; @Override protected void onResume() { super.onResume(); registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()); if (bluetoothService != null) { final boolean result = bluetoothService.connect(deviceAddress); Log.d(TAG, "Connect request result=" + result); } } @Override protected void onPause() { super.onPause(); unregisterReceiver(gattUpdateReceiver); } private static IntentFilter makeGattUpdateIntentFilter() { final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); return intentFilter; } }
Dalam Mentransfer data BLE,
BroadcastReceiver
juga digunakan untuk mengomunikasikan penemuan layanan serta
data karakteristik dari perangkat.
Tutup koneksi GATT
Salah satu langkah penting saat menangani koneksi Bluetooth adalah menutup
koneksi setelah Anda selesai menggunakannya. Untuk melakukannya, panggil fungsi close()
pada objek BluetoothGatt
. Pada contoh berikut, layanan
menyimpan referensi ke BluetoothGatt
. Saat aktivitas terlepas dari
layanan, koneksi akan ditutup agar baterai perangkat tidak habis.
Kotlin
class BluetoothLeService : Service() { ... override fun onUnbind(intent: Intent?): Boolean { close() return super.onUnbind(intent) } private fun close() { bluetoothGatt?.let { gatt -> gatt.close() bluetoothGatt = null } } }
Java
class BluetoothLeService extends Service { ... @Override public boolean onUnbind(Intent intent) { close(); return super.onUnbind(intent); } private void close() { if (bluetoothGatt == null) { Return; } bluetoothGatt.close(); bluetoothGatt = null; } }