BLE cihazıyla etkileşim kurmanın ilk adımı, cihaza bağlanmaktır. Daha
açık bir ifadeyle, cihazdaki GATT sunucusuna bağlanma. Bir BDE cihazındaki GATT sunucusuna bağlanmak için connectGatt() yöntemini kullanın. Bu yöntem üç parametre alır: Context nesnesi, autoConnect (BLE cihazı kullanılabilir hale gelir gelmez otomatik olarak bağlanılıp bağlanılmayacağını belirten bir boole değeri) ve BluetoothGattCallback referansı:
var bluetoothGatt: BluetoothGatt? = null // ... bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback)
Bu, BDE cihazı tarafından barındırılan GATT sunucusuna bağlanır ve BluetoothGatt örneğini döndürür. Bu örneği daha sonra GATT istemci işlemlerini gerçekleştirmek için kullanabilirsiniz. Arayan (Android uygulaması) GATT istemcisidir. BluetoothGattCallback, bağlantı durumu gibi sonuçları ve diğer tüm GATT istemci işlemlerini istemciye iletmek için kullanılır.
Bağlama hizmeti ayarlama
Aşağıdaki örnekte, BLE uygulaması Bluetooth cihazlarına bağlanmak, cihaz verilerini görüntülemek ve cihaz tarafından desteklenen GATT hizmetlerini ve özelliklerini görüntülemek için bir etkinlik (DeviceControlActivity) sağlar. Bu etkinlik, kullanıcı girişine bağlı olarak BluetoothLeService adlı bir Service ile iletişim kurar. Bu Service, BLE API aracılığıyla BLE cihazıyla etkileşime girer. İletişim, etkinliğin BluetoothLeService'ye bağlanmasına ve işlevlerin cihazlara bağlanmasına olanak tanıyan bir bağlı hizmet kullanılarak gerçekleştirilir. BluetoothLeService, etkinlik için hizmete erişim sağlayan bir Binder uygulamasına ihtiyaç duyar.
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 } } }
Etkinlik, bindService() kullanarak hizmeti başlatabilir. Hizmeti başlatmak için Intent iletir, bağlantı ve bağlantı kesme etkinliklerini dinlemek için ServiceConnection uygulaması ve ek bağlantı seçeneklerini belirtmek için bir işaret iletir.
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) } }
BluetoothAdapter'ı ayarlama
Hizmet bağlandıktan sonra BluetoothAdapter'e erişmesi gerekir. Adaptörün cihazda kullanılabilir olup olmadığını kontrol etmelidir. BluetoothAdapter hakkında daha fazla bilgi edinmek için Bluetooth'u ayarlama başlıklı makaleyi okuyun. Aşağıdaki örnekte bu kurulum kodu, başarıyı belirten bir Boolean değeri döndüren bir initialize() işlevine sarmalanmıştır.
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 } // ... }
Etkinlik, ServiceConnection uygulamasında bu işlevi çağırır.
initialize() işlevinden gelen yanlış bir dönüş değerini işleme, uygulamanıza bağlıdır. Kullanıcıya, mevcut cihazın Bluetooth işlemini desteklemediğini belirten bir hata mesajı gösterebilir veya Bluetooth'un çalışmasını gerektiren özellikleri devre dışı bırakabilirsiniz. Aşağıdaki örnekte, kullanıcıyı önceki ekrana geri göndermek için etkinlikte
finish() çağrılır.
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 } } // ... }
Bir cihaza bağlan
BluetoothLeService örneği başlatıldıktan sonra BLE cihazına bağlanabilir. Etkinliğin, bağlantıyı başlatabilmesi için cihaz adresini hizmete göndermesi gerekir. Hizmet, cihaza erişmek için önce getRemoteDevice()
BluetoothAdapter'ı arar. Adaptör, bu adrese sahip bir cihaz bulamazsa getRemoteDevice(), IllegalArgumentException hatası verir.
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 return true } ?: run { Log.w(TAG, "BluetoothAdapter not initialized") return false } }
DeviceControlActivity, hizmet başlatıldıktan sonra bu connect() işlevini çağırır. Etkinliğin, BLE cihazının adresini iletmesi gerekir. Aşağıdaki örnekte, cihaz adresi etkinliğe intent ekstrası olarak iletilir.
// 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 deviceAddress?.let { bluetooth.connect(it) } } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } }
GATT geri çağırmasını bildir
Etkinlik, hizmete hangi cihaza bağlanacağını bildirdikten ve hizmet cihaza bağlandıktan sonra hizmetin BLE cihazındaki GATT sunucusuna bağlanması gerekir. Bu bağlantı, bağlantı durumu, hizmet keşfi, özellik okumaları ve özellik bildirimleri hakkında bildirim almak için BluetoothGattCallback gerektirir.
Bu konu, bağlantı durumu bildirimlerine odaklanmaktadır. Hizmet keşfi, özellik okuma ve özellik bildirimi isteğinde bulunma hakkında bilgi edinmek için BLE verilerini aktarma başlıklı makaleyi inceleyin.
Cihazın GATT sunucusuyla bağlantı değiştiğinde onConnectionStateChange() işlevi tetiklenir.
Aşağıdaki örnekte geri çağırma, Service sınıfında tanımlanır. Böylece, hizmet bağlandıktan sonra BluetoothDevice ile kullanılabilir.
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 } } }
GATT hizmetine bağlanma
BluetoothGattCallback ilan edildikten sonra hizmet, cihazdaki GATT hizmetine bağlanmak için connect() işlevinden BluetoothDevice nesnesini kullanabilir.
connectGatt() işlevi kullanılır. Bu işlem için Context nesnesi, autoConnect boole değeri ve BluetoothGattCallback gerekir. Bu örnekte, uygulama doğrudan BLE cihazına bağlandığı için autoConnect için false iletilir.
Bir BluetoothGatt özelliği de eklenir. Bu, hizmetin artık ihtiyaç duyulmadığında bağlantıyı kapatmasına olanak tanır.
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 } } }
Yayın güncellemeleri
Sunucu, GATT sunucusuna bağlandığında veya sunucudan bağlantısı kesildiğinde yeni durumun etkinliğini bildirmesi gerekir. Bunu yapmanın birkaç yolu vardır. Aşağıdaki örnekte, hizmetten etkinliğe bilgi göndermek için yayınlar kullanılmaktadır.
Hizmet, yeni durumu yayınlamak için bir işlev bildirir. Bu işlev, sisteme yayınlanmadan önce bir Intent nesnesine iletilen bir işlem dizesi alır.
private fun broadcastUpdate(action: String) { val intent = Intent(action) sendBroadcast(intent) }
Yayın işlevi kullanıma sunulduktan sonra, GATT sunucusuyla bağlantı durumu hakkında bilgi göndermek için BluetoothGattCallback içinde kullanılır. Sabitler ve hizmetin mevcut bağlantı durumu, Intent işlemlerini temsil eden hizmette bildirilir.
class BluetoothLeService : Service() { private var connectionState = STATE_DISCONNECTED 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 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 } }
Etkinliklerdeki güncellemeleri dinleme
Hizmet, bağlantı güncellemelerini yayınladıktan sonra etkinliğin BroadcastReceiver uygulaması gerekir.
Etkinliği ayarlarken bu alıcıyı kaydedin ve etkinlik ekrandan ayrılırken kaydını silin. Etkinlik, hizmetten gelen etkinlikleri dinleyerek kullanıcı arayüzünü BLE cihazıyla mevcut bağlantı durumuna göre güncelleyebilir.
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()) bluetoothService?.let { service -> deviceAddress?.let { address -> val result = service.connect(address) Log.d(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) } } }
BLE verilerini aktarma bölümünde, BroadcastReceiver, hizmet keşfinin yanı sıra cihazdaki karakteristik verileri iletmek için de kullanılır.
GATT bağlantısını kapatma
Bluetooth bağlantılarıyla çalışırken önemli bir adım da bağlantıyı işiniz bittiğinde kapatmaktır. Bunu yapmak için BluetoothGatt nesnesinde close() işlevini çağırın. Aşağıdaki örnekte, hizmet BluetoothGatt referansını tutuyor. Etkinlik hizmetten ayrıldığında cihazın pilinin bitmesini önlemek için bağlantı kapatılır.
class BluetoothLeService : Service() { // ... override fun onUnbind(intent: Intent?): Boolean { close() return super.onUnbind(intent) } private fun close() { bluetoothGatt?.let { gatt -> gatt.close() bluetoothGatt = null } } }