البحث عن أجهزة تتضمّن بلوتوث

باستخدام BluetoothAdapter، يمكنك العثور على أجهزة بلوتوث عن بُعد، إما من خلال اكتشاف الجهاز أو من خلال الاستعلام عن قائمة الأجهزة المقترنة.

تأكَّد من امتلاكك أذونات البلوتوث المناسبة وإعداد تطبيقك لاستخدام البلوتوث قبل محاولة العثور على أجهزة تتضمّن بلوتوث.

اكتشاف الجهاز هو إجراء بحث يبحث في المنطقة المحلية عن الأجهزة المزوّدة بتقنية البلوتوث ويطلب بعض المعلومات حول كل جهاز. يُشار أحيانًا إلى هذه العملية باسم الاكتشاف أو الاستعلام أو الفحص. لا يستجيب جهاز بلوتوث مجاور لطلب الاكتشاف إلا إذا كان يقبل حاليًا طلبات الحصول على المعلومات من خلال قابلية اكتشافه. فإذا كان الجهاز قابلاً للاكتشاف، فإنه يستجيب لطلب الاكتشاف من خلال مشاركة بعض المعلومات، مثل اسم الجهاز وفئته وعنوان MAC الفريد. باستخدام هذه المعلومات، يمكن للجهاز الذي يجري عملية الاكتشاف اختيار بدء اتصال بالجهاز الذي تم اكتشافه.

ولأن الأجهزة القابلة للاكتشاف قد تكشف معلومات عن الموقع الجغرافي للمستخدم، تتطلب عملية اكتشاف الجهاز الوصول إلى الموقع الجغرافي. إذا كان يتم استخدام تطبيقك على جهاز يعمل بنظام التشغيل Android 8.0 (المستوى 26 من واجهة برمجة التطبيقات) أو إصدار أحدث، يمكنك استخدام واجهة برمجة تطبيقات "مدير الجهاز المصاحب" بدلاً من ذلك. يمكن لواجهة برمجة التطبيقات هذه رصد الجهاز نيابةً عن تطبيقك، لذلك لا يحتاج تطبيقك إلى طلب أذونات تحديد الموقع الجغرافي.

وبعد إجراء الاتصال باستخدام جهاز بعيد لأول مرة، يتم تقديم طلب الإقران تلقائيًا للمستخدم. عند إقران جهاز، يتم حفظ المعلومات الأساسية حول هذا الجهاز، مثل اسم الجهاز وفئته وعنوان MAC، ويمكن قراءتها باستخدام واجهات برمجة التطبيقات Bluetooth. باستخدام عنوان MAC المعروف لجهاز بعيد، يمكن بدء اتصال به في أي وقت بدون إجراء اكتشاف، على افتراض أن الجهاز لا يزال ضمن النطاق.

يُرجى العلم أنّ هناك فرقًا بين عملية الإقران وعدم الربط:

  • ويعني الإقران أنّ هناك جهازين وهم يدركان وجود بعضهما، ويتوفّر مفتاح رابط مشترك يمكن استخدامه للمصادقة، وهما قادران على إنشاء اتصال مشفّر ببعضهما البعض.
  • يعني الاتصال أنّ الأجهزة تشترك حاليًا في قناة بروتوكول RFCOMM ويمكنها نقل البيانات مع بعضها البعض. تتطلب واجهات برمجة تطبيقات Bluetooth الحالية إقران الأجهزة قبل التمكن من إنشاء اتصال RFCOMM. يتم تنفيذ الإقران تلقائيًا عند بدء اتصال مشفّر بواجهات برمجة تطبيقات Bluetooth.

توضح الأقسام التالية كيفية العثور على الأجهزة التي تم إقرانها وكيفية اكتشاف أجهزة جديدة باستخدام اكتشاف الأجهزة.

الاستعلام عن الأجهزة المقترِنة

قبل اكتشاف الجهاز، يجدر بك الاستعلام عن مجموعة من الأجهزة المقترنة لمعرفة ما إذا كان الجهاز المطلوب معروفًا بالفعل أم لا. ولإجراء ذلك، يمكنك الاتصال بالرقم getBondedDevices(). يؤدي ذلك إلى عرض مجموعة من الكائنات BluetoothDevice التي تمثل الأجهزة المقترنة. على سبيل المثال، يمكنك الاستعلام عن جميع الأجهزة المقترنة والحصول على اسم وعنوان MAC لكل جهاز، كما يوضح مقتطف الرمز التالي:

Kotlin

val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
pairedDevices?.forEach { device ->
   val deviceName = device.name
   val deviceHardwareAddress = device.address // MAC address
}

Java

Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

if (pairedDevices.size() > 0) {
   // There are paired devices. Get the name and address of each paired device.
   for (BluetoothDevice device : pairedDevices) {
       String deviceName = device.getName();
       String deviceHardwareAddress = device.getAddress(); // MAC address
   }
}

لبدء الاتصال بجهاز يتضمّن بلوتوث، كل ما تحتاجه من كائن BluetoothDevice المرتبط هو عنوان MAC، الذي يمكنك استرداده من خلال استدعاء getAddress(). يمكنك معرفة المزيد من المعلومات عن إنشاء اتصال في ربط أجهزة بلوتوث.

اكتشاف الأجهزة

لبدء استكشاف الأجهزة، يُرجى الاتصال بالرقم startDiscovery(). وهذه العملية غير متزامنة وتعرض قيمة منطقية تشير إلى ما إذا كان الاكتشاف قد بدأ بنجاح أم لا. تتضمن عملية الاكتشاف عادةً فحصًا للاستعلام لمدة 12 ثانية تقريبًا، يليه فحص صفحة لكل جهاز تم العثور عليه لاسترداد اسم البلوتوث.

لتلقّي معلومات حول كل جهاز يتم اكتشافه، يجب أن يسجّل تطبيقك BroadcastReceiver لهدف ACTION_FOUND. يبث النظام هذا الغرض لكل جهاز. يحتوي الغرض على الحقلَين الإضافيَين EXTRA_DEVICE وEXTRA_CLASS، اللذين يتضمّنان بدورهما BluetoothDevice وBluetoothClass على التوالي. يعرض مقتطف الرمز التالي كيفية التسجيل للتعامل مع البث عند اكتشاف الأجهزة:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
   ...

   // Register for broadcasts when a device is discovered.
   val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
   registerReceiver(receiver, filter)
}

// Create a BroadcastReceiver for ACTION_FOUND.
private val receiver = object : BroadcastReceiver() {

   override fun onReceive(context: Context, intent: Intent) {
       val action: String = intent.action
       when(action) {
           BluetoothDevice.ACTION_FOUND -> {
               // Discovery has found a device. Get the BluetoothDevice
               // object and its info from the Intent.
               val device: BluetoothDevice =
                       intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
               val deviceName = device.name
               val deviceHardwareAddress = device.address // MAC address
           }
       }
   }
}

override fun onDestroy() {
   super.onDestroy()
   ...

   // Don't forget to unregister the ACTION_FOUND receiver.
   unregisterReceiver(receiver)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
   ...

   // Register for broadcasts when a device is discovered.
   IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
   registerReceiver(receiver, filter);
}

// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
   public void onReceive(Context context, Intent intent) {
       String action = intent.getAction();
       if (BluetoothDevice.ACTION_FOUND.equals(action)) {
           // Discovery has found a device. Get the BluetoothDevice
           // object and its info from the Intent.
           BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
           String deviceName = device.getName();
           String deviceHardwareAddress = device.getAddress(); // MAC address
       }
   }
};

@Override
protected void onDestroy() {
   super.onDestroy();
   ...

   // Don't forget to unregister the ACTION_FOUND receiver.
   unregisterReceiver(receiver);
}

لبدء اتصال مع جهاز يتضمّن بلوتوث، يمكنك الاتصال بـ getAddress() على BluetoothDevice لاسترداد عنوان MAC المرتبط.

تفعيل قابلية الاكتشاف

ولجعل الجهاز المحلي قابلاً للاكتشاف على الأجهزة الأخرى، اتّصِل بـ startActivityForResult(Intent, int) مع توضيح الغرض ACTION_REQUEST_DISCOVERABLE. يؤدي هذا إلى إصدار طلب لتفعيل وضع اكتشاف النظام بدون الحاجة إلى الانتقال إلى تطبيق "الإعدادات"، ما سيؤدي إلى إيقاف تطبيقك. ويصبح الجهاز قابلاً للاكتشاف تلقائيًا لمدة دقيقتين. يمكنك تحديد مدة مختلفة، مثل ساعة واحدة، من خلال إضافة السمة EXTRA_DISCOVERABLE_DURATION الإضافية.

يضبط مقتطف الرمز التالي الجهاز ليكون قابلاً للاكتشاف لمدة خمس دقائق:

Kotlin

val requestCode = 1;
val discoverableIntent: Intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).apply {
   putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
}
startActivityForResult(discoverableIntent, requestCode)

Java

int requestCode = 1;
Intent discoverableIntent =
       new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(discoverableIntent, requestCode);


الشكل 2: مربّع حوار تفعيل قابلية الاكتشاف.

يتم عرض مربع حوار يطلب إذن المستخدم لجعل الجهاز قابلاً للاكتشاف، كما هو موضح في الشكل 2. إذا ردّ المستخدم على السؤال "سماح"، سيصبح الجهاز قابلاً للاكتشاف خلال الفترة الزمنية المحدّدة. بعد ذلك، سيتلقّى نشاطك مكالمة لمعاودة الاتصال بـ onActivityResult() مع رمز نتيجة يساوي المدة التي يمكن خلالها اكتشاف الجهاز. إذا ردّ المستخدم بـ "رفض" أو إذا حدث خطأ، سيكون رمز النتيجة هو RESULT_CANCELED.

يظل الجهاز في وضع الاكتشاف بدون إشعارات خلال الوقت المخصص. لتلقّي إشعار عند تغيير وضع الاكتشاف، عليك تسجيل BroadcastReceiver لهدف ACTION_SCAN_MODE_CHANGED. يحتوي هذا الغرض على الحقلَين الإضافيَّين EXTRA_SCAN_MODE وEXTRA_PREVIOUS_SCAN_MODE، اللذين يوفّران وضعَي البحث الجديد والقديم على التوالي. القيم المحتملة لكل عنصر إضافي هي كما يلي:

SCAN_MODE_CONNECTABLE_DISCOVERABLE
الجهاز في وضع الاكتشاف.
SCAN_MODE_CONNECTABLE
الجهاز ليس في وضع الاكتشاف ولكن لا يزال بإمكانه تلقّي اتصالات.
SCAN_MODE_NONE
الجهاز ليس في وضع الاكتشاف ولا يمكنه تلقّي اتصالات.

إذا بدأت الاتصال بجهاز بعيد، لن تحتاج إلى تفعيل قابلية اكتشاف الجهاز. ولا يكون تفعيل قابلية الاكتشاف ضروريًا إلا عندما تريد أن يستضيف تطبيقك مقبس خادم يقبل الاتصالات الواردة، فيجب أن تكون الأجهزة البعيدة قادرة على اكتشاف الأجهزة الأخرى قبل بدء الاتصالات بتلك الأجهزة الأخرى.