تتمثل الخطوة الأولى للتفاعل مع جهاز BLE في الاتصال به. المزيد
تحديدًا، الاتصال بخادم GATT على الجهاز. للربط ببروتوكول GATT
على جهاز BLE، استخدِم
connectGatt()
. تستخدم هذه الطريقة ثلاث معلمات: a
كائن Context
، autoConnect
(قيمة منطقية
للإشارة إلى ما إذا كان سيتم الاتصال تلقائيًا بجهاز BLE
متاحة)، ومرجعًا
BluetoothGattCallback
:
var bluetoothGatt: BluetoothGatt? = null
...
bluetoothGatt = device.connectGatt(this, false, gattCallback)
bluetoothGatt = device.connectGatt(this, false, gattCallback);
يتصل هذا بخادم GATT الذي يستضيفه جهاز BLE، ويعرض
مثيل 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
}
}
}
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
التنفيذ للاستماع إلى أحداث الاتصال وانقطاع الاتصال، وعلامة
لتحديد خيارات اتصال إضافية.
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)
}
}
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);
}
}
إعداد Bluetooth Adapter
بعد ربط الخدمة، يجب الوصول إلى
BluetoothAdapter
يجب
تأكَّد من توفُّر المحوّل على الجهاز. اقرأ الإعداد
Bluetooth لمزيد من المعلومات حول
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
}
...
}
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
.
وتعتمد معالجة القيمة المعروضة الخاطئة من الدالة 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
}
}
...
}
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
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
}
}
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. ضِمن
في المثال التالي، يتم تمرير عنوان الجهاز إلى النشاط على أنه هدف
المزيد.
// 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
}
}
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;
}
};
تعريف معاودة الاتصال بـ GATT
بعد أن يخبر النشاط الخدمة بالجهاز الذي يجب الاتصال به والخدمة
بالاتصال بالجهاز، تحتاج الخدمة إلى الاتصال بخادم GATT على
جهاز BLE. يتطلّب هذا الاتصال توفّر "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
}
}
}
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
. يتيح ذلك للخدمة إغلاق
اتصال عندما لا يكون
هناك حاجة أطول.
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
}
}
}
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
قبل البث
إلى النظام.
private fun broadcastUpdate(action: String) {
val intent = Intent(action)
sendBroadcast(intent)
}
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
وعندما تصبح وظيفة البث في مكانها، يتم استخدامها داخل
BluetoothGattCallback
لإرسال معلومات حول حالة الاتصال مع
GATT. يتم الإعلان عن الثوابت وحالة الاتصال الحالية للخدمة.
في الخدمة التي تمثل إجراءات Intent
.
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
}
}
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.
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)
}
}
}
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
إن إحدى الخطوات المهمة عند التعامل مع اتصالات البلوتوث هي إغلاق
الاتصال عند الانتهاء منه. لإجراء ذلك، يمكنك الاتصال بالرقم close()
.
على الكائن BluetoothGatt
. في المثال التالي، الخدمة
يشير إلى 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
}
}
}
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;
}
}