השלב הראשון באינטראקציה עם מכשיר BLE הוא התחברות אליו. סמל האפשרויות הנוספות
באופן ספציפי, התחברות לשרת GATT במכשיר. כדי להתחבר ל-GATT
במכשיר BLE, צריך להשתמש
connectGatt()
. השיטה הזו כוללת שלושה פרמטרים:
אובייקט Context
, autoConnect
(ערך בוליאני
שמציין אם להתחבר באופן אוטומטי למכשיר BLE ברגע
הופך לזמין), והפניה
BluetoothGattCallback
:
Kotlin
var bluetoothGatt: BluetoothGatt? = null ... bluetoothGatt = device.connectGatt(this, false, gattCallback)
Java
bluetoothGatt = device.connectGatt(this, false, gattCallback);
הפעולה הזאת מתחברת לשרת GATT שמתארח במכשיר ה-BLE ומחזירה
מופע BluetoothGatt
,
שבו תוכלו להשתמש כדי לבצע פעולות בלקוח GATT. מבצע הקריאה החוזרת (האפליקציה ל-Android)
הוא לקוח ה-GATT.
השדה BluetoothGattCallback
משמש כדי לספק תוצאות ללקוח, כמו
סטטוס החיבור, וכל פעילות נוספת של לקוח GATT.
הגדרת שירות קשור
בדוגמה הבאה, אפליקציית BLE מספקת פעילות
(DeviceControlActivity
) כדי להתחבר למכשירי Bluetooth, להציג את נתוני המכשיר,
ולהציג את השירותים והמאפיינים של GATT שנתמכים על ידי המכשיר. מבוסס
בקלט של המשתמשים, פעילות זו מתקשרת עם
Service
בשם BluetoothLeService
,
מקיים אינטראקציה עם מכשיר BLE באמצעות BLE API. התקשורת היא
מתבצע באמצעות שירות מקושר, שמאפשר
את הפעילות שמחברים אל BluetoothLeService
וקוראים לפונקציות
להתחבר למכשירים. ל-BluetoothLeService
נדרש
הטמעת Binder
שמעניקה גישה אל
השירות עבור הפעילות.
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; } } }
הפעילות יכולה להפעיל את השירות באמצעות
bindService()
מעביר Intent
כדי להתחיל
שירות, ServiceConnection
ההטמעה כדי להאזין לאירועי החיבור והניתוק, וגם דגל
כדי לציין אפשרויות חיבור נוספות.
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); } }
הגדרת BluetoothAdapter
אחרי שהשירות מקושר, הוא צריך לגשת
BluetoothAdapter
היא צריכה
ולבדוק שהמתאם זמין במכשיר. מומלץ לקרוא את המאמר הגדרה
Bluetooth לקבלת מידע נוסף על
BluetoothAdapter
. הדוגמה הבאה כוללת את קוד ההגדרה הזה
הפונקציה initialize()
שמחזירה את הערך Boolean
שמציין שהפעולה הסתיימה בהצלחה.
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; } ... }
הפעילות מפעילה את הפונקציה הזו בתוך ההטמעה של ServiceConnection
.
הטיפול בערך המוחזר 'FALSE' מהפונקציה initialize()
תלוי
תרגום מכונה. אפשר להציג הודעת שגיאה למשתמש שמציינת
המכשיר הנוכחי לא תומך בפעולת Bluetooth או משבית תכונות כלשהן
שדורשים Bluetooth כדי לפעול. בדוגמה הבאה,
בוצעה קריאה ל-finish()
בפעילות
כדי לשלוח את המשתמש חזרה למסך הקודם.
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; } }; ... }
התחברות למכשיר
אחרי שמכונת BluetoothLeService
מופעלת, היא יכולה להתחבר ל-BLE
במכשיר. הפעילות צריכה לשלוח את כתובת המכשיר לשירות כדי
מפעילים את החיבור. השירות יתקשר בפעם הראשונה
getRemoteDevice()
במכשיר BluetoothAdapter
כדי לגשת למכשיר. אם המתאם לא מוצא
מכשיר עם הכתובת הזו, getRemoteDevice()
מוסיף
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
קורא לפונקציה connect()
ברגע שהשירות
אותחל. הפעילות צריכה לעבור בכתובת של מכשיר ה-BLE. לחשבון
בדוגמה הבאה, כתובת המכשיר מועברת לפעילות כ-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; } };
הצהרה על קריאה חוזרת (callback) של GATT
לאחר שהפעילות מנחה את השירות לאיזה מכשיר להתחבר ולאיזה שירות
מתחבר למכשיר, השירות צריך להתחבר לשרת GATT
מכשיר BLE. נדרש BluetoothGattCallback
כדי לקבל את החיבור הזה
התראות על מצב החיבור, גילוי שירות, מאפיין
קריאה והתראות אופייניות.
הנושא הזה מתמקד בהתראות על מצב החיבור. מידע נוסף זמין בקטע העברת BLE נתונים לגבי הביצועים גילוי שירותים, קריאות של מאפיינים ומאפייני בקשות התראות.
onConnectionStateChange()
הפונקציה מופעלת כשהחיבור לשרת GATT של המכשיר משתנה.
בדוגמה הבאה, הקריאה החוזרת מוגדרת במחלקה Service
, כך
ניתן להשתמש בו בשילוב עם
BluetoothDevice
אחרי
מתחבר אליו.
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 } } };
התחברות לשירות GATT
לאחר ההצהרה על BluetoothGattCallback
, השירות יכול להשתמש ב
אובייקט BluetoothDevice
מהפונקציה connect()
כדי להתחבר ל-GATT
שירות במכשיר.
connectGatt()
. הפעולה הזו מחייבת אובייקט Context
, ערך בוליאני autoConnect
דגל, ואת BluetoothGattCallback
. בדוגמה הזאת, האפליקציה
מתחבר למכשיר BLE, ולכן false
מועבר למשך autoConnect
.
נוסף גם נכס BluetoothGatt
. כך השירות יכול לסגור את
חיבור כשאין חיבור לאינטרנט.
לזמן ממושך יותר.
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; } } }
עדכונים לגבי שידור
כשהשרת מתחבר לשרת GATT או מתנתק ממנו, הוא צריך להודיע על כך את הפעילות של המצב החדש. יש כמה דרכים לעשות זאת. הדוגמה הבאה משתמשת בשידורים כדי לשלוח את מידע מהשירות ועד לפעילות.
השירות מצהיר על פונקציה לשידור המצב החדש. הפונקציה הזו לוקחת
במחרוזת פעולה שמועברת לאובייקט Intent
לפני השידור
למערכת.
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); }
ברגע שפונקציית השידור נמצאת במקום, משתמשים בה בתוך
BluetoothGattCallback
כדי לשלוח מידע על מצב החיבור עם
שרת GATT. קבועים ומצב החיבור הנוכחי של השירות מוצהרים
בשירות שמייצג את הפעולות של 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); } } }; … }
האזנה לעדכונים בפעילות
לאחר שהשירות משדר את החיבור מתעדכן, הפעילות צריכה
להטמיע BroadcastReceiver
.
רישום המקבל הזה בעת הגדרת הפעילות וביטול הרישום שלו כאשר
מוצגת פעילות מהמסך. על ידי האזנה לאירועים מהשירות,
הפעילות יכולה לעדכן את ממשק המשתמש בהתאם
על מצב החיבור של מכשיר ה-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; } }
בקטע העברת נתוני BLE,
BroadcastReceiver
משמש גם להעברת גילוי השירות בתור
וגם את הנתונים המאפיינים מהמכשיר.
סגירת חיבור GATT
אחד השלבים החשובים כשמדובר בחיבורי Bluetooth הוא לסגור את
אחרי שמסיימים איתו. כדי לעשות זאת, צריך להתקשר אל close()
באובייקט BluetoothGatt
. בדוגמה הבאה, השירות
מכיל את ההפניה אל BluetoothGatt
. לאחר ביטול הקישור של הפעילות אל
השירות, החיבור נסגר כדי למנוע את התרוקנות הסוללה של המכשיר.
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; } }