查找蓝牙设备

使用 BluetoothAdapter,您可以通过设备发现或查询配对设备列表来查找远程蓝牙设备。

在尝试查找蓝牙设备之前,请确保您拥有适当的 蓝牙权限为您的应用设置蓝牙

设备发现是一种扫描过程,用于搜索本地区域中的蓝牙设备并请求有关每个设备的一些信息。此过程有时称为发现、询问或扫描。附近的蓝牙设备仅当它当前正在接受信息请求(即可发现状态)时才会响应发现请求。如果设备可发现,它会通过共享一些信息(例如设备名称、设备类别和唯一的 MAC 地址)来响应发现请求。使用此信息,执行发现过程的设备可以选择启动与已发现设备的连接。

由于可发现的设备可能会泄露有关用户位置的信息,因此设备发现过程需要位置访问权限。如果您的应用正在运行 Android 8.0(API 级别 26)或更高版本的设备上使用,请考虑改用 配套设备管理器 API。此 API 代表您的应用执行设备发现,因此您的应用无需 请求位置权限

首次与远程设备建立连接后,系统会自动向用户显示配对请求。当设备配对后,有关该设备的基本信息(例如设备名称、类别和 MAC 地址)将被保存,并可以使用蓝牙 API 读取。使用远程设备的已知 MAC 地址,可以在任何时候启动与它的连接,而无需执行发现,前提是该设备仍在范围内。

请注意,配对和连接之间存在差异。

  • 配对 表示两个设备知道彼此的存在,具有可用于身份验证的共享链接密钥,并且能够彼此建立加密连接。
  • 连接 表示设备当前共享一个 RFCOMM 通道,并且能够彼此传输数据。当前的蓝牙 API 要求设备在建立 RFCOMM 连接之前必须配对。当您使用蓝牙 API 启动加密连接时,会自动执行配对。

以下部分介绍如何查找已配对的设备以及如何使用设备发现发现新设备。

查询配对设备

在执行设备发现之前,值得查询已配对设备的集合,以查看所需的设备是否已知。为此,请调用 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 秒的查询扫描,然后对找到的每个设备进行页面扫描以检索其蓝牙名称。

要接收有关每个发现设备的信息,您的应用必须为 ACTION_FOUND 意图注册一个 BroadcastReceiver。系统会为每个设备广播此意图。该意图包含额外字段 EXTRA_DEVICEEXTRA_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);
}

要与蓝牙设备建立连接,请在 BluetoothDevice 上调用 getAddress() 以检索关联的 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

设备会在分配的时间内静默保持可发现模式。要接收可发现模式已更改的通知,请为 ACTION_SCAN_MODE_CHANGED 意图注册一个 BroadcastReceiver。此意图包含额外字段 EXTRA_SCAN_MODEEXTRA_PREVIOUS_SCAN_MODE,它们分别提供新的和旧的扫描模式。每个额外字段的可能值为:

SCAN_MODE_CONNECTABLE_DISCOVERABLE
设备处于可发现模式。
SCAN_MODE_CONNECTABLE
设备未处于可发现模式,但仍可以接收连接。
SCAN_MODE_NONE
设备未处于可发现模式,并且无法接收连接。

如果您要发起与远程设备的连接,则无需启用设备可发现性。仅当您希望您的应用托管一个接受传入连接的服务器套接字时,才需要启用可发现性,因为远程设备必须能够发现其他设备才能发起与这些其他设备的连接。