किसी BLE डिवाइस से इंटरैक्ट करने के लिए, सबसे पहले उससे कनेक्ट करना ज़रूरी है. खास तौर पर, डिवाइस पर GATT सर्वर से कनेक्ट करने के लिए. किसी BLE डिवाइस पर GATT सर्वर से कनेक्ट करने के लिए, connectGatt() तरीके का इस्तेमाल करें. इस तरीके में तीन पैरामीटर होते हैं: Context ऑब्जेक्ट, autoConnect (एक बूलियन जो यह बताता है कि BLE डिवाइस के उपलब्ध होते ही, उससे अपने-आप कनेक्ट होना है या नहीं), और BluetoothGattCallback का रेफ़रंस:
var bluetoothGatt: BluetoothGatt? = null // ... bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback)
यह बीएलई डिवाइस से होस्ट किए गए GATT सर्वर से कनेक्ट होता है और BluetoothGatt इंस्टेंस दिखाता है. इसके बाद, GATT क्लाइंट से जुड़ी कार्रवाइयां करने के लिए, इसका इस्तेमाल किया जा सकता है. कॉल करने वाला (Android ऐप्लिकेशन), GATT क्लाइंट होता है. BluetoothGattCallback का इस्तेमाल, क्लाइंट को नतीजे डिलीवर करने के लिए किया जाता है. जैसे, कनेक्शन की स्थिति के साथ-साथ GATT क्लाइंट की अन्य कार्रवाइयां.
सीमित सेवा सेट अप करना
यहां दिए गए उदाहरण में, BLE ऐप्लिकेशन एक गतिविधि (DeviceControlActivity) उपलब्ध कराता है. इससे ब्लूटूथ डिवाइसों से कनेक्ट किया जा सकता है, डिवाइस का डेटा दिखाया जा सकता है, और डिवाइस पर काम करने वाली GATT सेवाएं और विशेषताएं दिखाई जा सकती हैं. उपयोगकर्ता के इनपुट के आधार पर, यह गतिविधि Service के साथ कम्यूनिकेट करती है. इसे BluetoothLeService कहा जाता है. यह BLE एपीआई का इस्तेमाल करके, BLE डिवाइस के साथ इंटरैक्ट करता है. सीमित सेवा, जो गतिविधि को BluetoothLeService से कनेक्ट करने और डिवाइसों से कनेक्ट करने के लिए फ़ंक्शन को कॉल करने की अनुमति देती है, इस कम्यूनिकेशन को मैनेज करती है. BluetoothLeService को Binder को लागू करना ज़रूरी है, जो गतिविधि के लिए सेवा का ऐक्सेस देता है.
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 } } }
गतिविधि, bindService() का इस्तेमाल करके सेवा शुरू कर सकती है. इसके लिए, सेवा शुरू करने के लिए Intent, कनेक्शन और डिसकनेक्शन इवेंट को सुनने के लिए ServiceConnection लागू करना, और कनेक्शन के अन्य विकल्पों की जानकारी देने के लिए एक फ़्लैग पास करना होगा.
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 सेट अप करना
गतिविधि को सेवा से बाइंड करने के बाद, सेवा को BluetoothAdapter को ऐक्सेस करना होगा. यह जांच करनी चाहिए कि डिवाइस पर अडैप्टर उपलब्ध है या नहीं. BluetoothAdapter के बारे में ज़्यादा जानने के लिए, ब्लूटूथ सेट अप करना लेख पढ़ें. यहां दिए गए उदाहरण में, इस सेटअप कोड को initialize() फ़ंक्शन में रैप किया गया है. यह फ़ंक्शन, Boolean वैल्यू दिखाता है, जिससे पता चलता है कि सेटअप पूरा हो गया है.
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 } // ... }
गतिविधि, इस फ़ंक्शन को अपने ServiceConnection लागू करने के दौरान कॉल करती है.
initialize() फ़ंक्शन से मिलने वाली गलत रिटर्न वैल्यू को मैनेज करना, आपके ऐप्लिकेशन पर निर्भर करता है. उपयोगकर्ता को गड़बड़ी का मैसेज दिखाकर यह बताया जा सकता है कि मौजूदा डिवाइस पर ब्लूटूथ की सुविधा काम नहीं करती. इसके अलावा, ब्लूटूथ की मदद से काम करने वाली सुविधाओं को बंद किया जा सकता है. यहां दिए गए उदाहरण में, उपयोगकर्ता को पिछली स्क्रीन पर वापस भेजने के लिए, गतिविधि finish() को कॉल करती है.
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 } } // ... }
किसी डिवाइस से कनेक्ट करें
BluetoothLeService इंस्टेंस शुरू होने के बाद, यह BLE डिवाइस से कनेक्ट हो सकता है. गतिविधि को सेवा के लिए डिवाइस का पता भेजना होगा, ताकि कनेक्शन शुरू किया जा सके. डिवाइस को ऐक्सेस करने के लिए, सेवा पहले BluetoothAdapter पर getRemoteDevice() को कॉल करती है. अगर अडैप्टर को उस पते पर कोई डिवाइस नहीं मिलता है, तो getRemoteDevice(), IllegalArgumentException दिखाता है.
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 इस connect() फ़ंक्शन को कॉल करता है. गतिविधि को बीएलई डिवाइस का पता पास करना होगा. यहां दिए गए उदाहरण में, डिवाइस के पते को इंटेंट एक्स्ट्रा के तौर पर गतिविधि में पास किया गया है.
// 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 कॉलबैक का एलान करना
जब गतिविधि से सेवा को पता चलता है कि किस डिवाइस से कनेक्ट करना है और सेवा उस डिवाइस से कनेक्ट हो जाती है, तो सेवा को बीएलई डिवाइस पर GATT सर्वर से कनेक्ट करना होता है. इस कनेक्शन के लिए, BluetoothGattCallback की ज़रूरत होती है, ताकि कनेक्शन की स्थिति, डिवाइस और सेवाओं की अपने-आप पहचान करने, विशेषताओं को पढ़ने, और विशेषताओं से जुड़ी सूचनाएं मिल सकें.
इस दस्तावेज़ में, कनेक्शन की स्थिति से जुड़ी सूचनाओं के बारे में बताया गया है. सेवाओं को खोजने, विशेषताओं को पढ़ने, और विशेषताओं की सूचनाओं का अनुरोध करने का तरीका जानने के लिए, BLE डेटा ट्रांसफ़र करना लेख पढ़ें.
onConnectionStateChange() फ़ंक्शन तब ट्रिगर होता है, जब डिवाइस के GATT सर्वर से कनेक्शन बदलता है. यहां दिए गए उदाहरण में, कॉलबैक को Service क्लास में तय किया गया है, ताकि सेवा के इससे कनेक्ट होने के बाद, इसका इस्तेमाल BluetoothDevice के साथ किया जा सके.
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 सेवा से कनेक्ट करना
BluetoothGattCallback का एलान होने के बाद, सेवा connect() फ़ंक्शन से BluetoothDevice ऑब्जेक्ट का इस्तेमाल करके, डिवाइस पर GATT सेवा से कनेक्ट कर सकती है.
सेवा, connectGatt() फ़ंक्शन को कॉल करती है. इसके लिए, Context ऑब्जेक्ट, autoConnect बूलियन फ़्लैग, और BluetoothGattCallback की ज़रूरत होती है. इस उदाहरण में, ऐप्लिकेशन सीधे बीएलई डिवाइस से कनेक्ट हो रहा है. इसलिए, autoConnect के लिए false पास किया गया है.
यह सेवा एक BluetoothGatt प्रॉपर्टी भी जोड़ती है, ताकि ज़रूरत न होने पर कनेक्शन को बंद किया जा सके.
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 } } }
ब्रॉडकास्ट से जुड़े अपडेट
जब सेवा, GATT सर्वर से कनेक्ट या डिसकनेक्ट होती है, तो उसे नई स्थिति की गतिविधि के बारे में सूचना देनी होती है. ऐसा करने के कई तरीके हैं. इस उदाहरण में, सेवा से गतिविधि तक जानकारी भेजने के लिए, ब्रॉडकास्ट का इस्तेमाल किया गया है.
यह सेवा, नई स्थिति को ब्रॉडकास्ट करने के लिए एक फ़ंक्शन का एलान करती है. यह फ़ंक्शन, ऐक्शन स्ट्रिंग को इनपुट के तौर पर लेता है. इसे सिस्टम पर ब्रॉडकास्ट करने से पहले, Intent ऑब्जेक्ट को पास किया जाता है.
private fun broadcastUpdate(action: String) { val intent = Intent(action) sendBroadcast(intent) }
ब्रॉडकास्ट फ़ंक्शन सेट अप होने के बाद, सेवा इसका इस्तेमाल BluetoothGattCallback में करती है. इससे GATT सर्वर के साथ कनेक्शन की स्थिति के बारे में जानकारी भेजी जाती है. यह सेवा, Intent कार्रवाइयों और सेवा के मौजूदा कनेक्शन की स्थिति को दिखाने वाले कॉन्सटैंट तय करती है.
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 } }
गतिविधि में हुए बदलावों के बारे में सुनना
सेवा से कनेक्शन के अपडेट ब्रॉडकास्ट होने के बाद, गतिविधि को BroadcastReceiver लागू करना होगा. गतिविधि सेट अप करते समय इस रिसीवर को रजिस्टर करें. साथ ही, गतिविधि के स्क्रीन से हटने पर इसे अनरजिस्टर करें. सेवा से मिलने वाले इवेंट को सुनकर, गतिविधि उपयोगकर्ता इंटरफ़ेस को अपडेट कर सकती है. यह अपडेट, बीएलई डिवाइस के साथ मौजूदा कनेक्शन की स्थिति के आधार पर किया जाता है.
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) } } }
बीएलई डेटा ट्रांसफ़र करें में, BroadcastReceiver का इस्तेमाल डिवाइस से, डिवाइस और सेवाओं की अपने-आप पहचान करने के साथ-साथ, विशेषता वाले डेटा को कम्यूनिकेट करने के लिए भी किया जाता है.
GATT कनेक्शन बंद करना
ब्लूटूथ कनेक्शन का इस्तेमाल करते समय, एक ज़रूरी चरण यह है कि काम पूरा होने के बाद कनेक्शन को बंद कर दें. ऐसा करने के लिए, BluetoothGatt ऑब्जेक्ट पर close() फ़ंक्शन को कॉल करें. यहां दिए गए उदाहरण में, सेवा के पास BluetoothGatt का रेफ़रंस है. जब गतिविधि सेवा से अनबाइंड हो जाती है, तो डिवाइस की बैटरी खत्म होने से बचाने के लिए कनेक्शन बंद कर दिया जाता है.
class BluetoothLeService : Service() { // ... override fun onUnbind(intent: Intent?): Boolean { close() return super.onUnbind(intent) } private fun close() { bluetoothGatt?.let { gatt -> gatt.close() bluetoothGatt = null } } }