使用 BluetoothAdapter
,您可以通过设备发现或查询配对设备列表来查找远程蓝牙设备。
在尝试查找蓝牙设备之前,请确保您拥有适当的蓝牙权限并针对蓝牙设置您的应用。
设备发现是一种扫描过程,会在局部区域内搜索支持蓝牙的设备,并请求每种设备的一些信息。此过程有时称为发现、查询或扫描。 仅当附近的蓝牙设备目前正通过可检测性接受信息请求时,才会响应发现请求。如果设备可被检测到,它会通过分享一些信息(例如设备的名称、类及其唯一 MAC 地址)来响应发现请求。利用这些信息,执行发现过程的设备可以选择发起与被发现设备的连接。
由于可检测到的设备可能会泄露用户位置信息,因此设备发现过程需要位置信息访问权限。如果您的应用在搭载 Android 8.0(API 级别 26)或更高版本的设备上使用,请考虑改用 Companion Device Manager API。此 API 会代表您的应用执行设备发现,因此该应用无需请求位置信息权限。
在首次与远程设备建立连接后,系统会自动向用户显示配对请求。设备配对后,系统会保存有关该设备的基本信息(例如设备的名称、类和 MAC 地址),并且可使用 Bluetooth API 读取这些信息。借助远程设备的已知 MAC 地址,您可以随时向其发起与远程设备的连接,而无需执行发现操作(假定该设备仍在有效范围内)。
请注意,被配对与被连接之间存在区别:
- “已配对”意味着两台设备知晓彼此的存在,具有可用于身份验证的共享链路密钥,并且能够与彼此建立加密连接。
- 被连接是指设备当前共享一个 RFCOMM 通道,并且能够相互传输数据。当前的蓝牙 API 要求对设备进行配对,然后才能建立 RFCOMM 连接。使用 Bluetooth 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
intent 注册一个 BroadcastReceiver
。系统会为每个设备广播此 intent。该 intent 包含 extra 字段 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); }
如需发起与蓝牙设备的连接,您可以对 BluetoothDevice
调用 getAddress()
以检索关联的 MAC 地址。
启用可检测性
如需使本地设备可被其他设备检测到,请使用 ACTION_REQUEST_DISCOVERABLE
intent 调用 startActivityForResult(Intent, int)
。此操作会发出启用系统的可检测模式的请求,而无需前往“设置”应用,这会停止您自己的应用。默认情况下,设备在两分钟内会变为可检测到。您可以通过添加 EXTRA_DISCOVERABLE_DURATION
extra 定义不同的时长(最长为 1 小时)。
以下代码段会将设备设置为可检测到五分钟:
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 所示。如果用户响应“允许”,则设备在指定的时间段内变为可检测到。然后,您的 activity 会收到对 onActivityResult()
回调的调用,其结果代码等于设备可检测到的时长。如果用户响应“拒绝”或出现错误,结果代码为 RESULT_CANCELED
。
设备会在规定的时间内以静默方式保持可检测模式。如需在可检测到模式发生变化时收到通知,请为 ACTION_SCAN_MODE_CHANGED
intent 注册 BroadcastReceiver
。此 intent 包含额外字段 EXTRA_SCAN_MODE
和 EXTRA_PREVIOUS_SCAN_MODE
,分别提供新旧扫描模式。每个 extra 的可能值如下:
SCAN_MODE_CONNECTABLE_DISCOVERABLE
- 设备处于可检测到模式。
SCAN_MODE_CONNECTABLE
- 设备未处于可检测到模式,但仍然可以接收连接。
SCAN_MODE_NONE
- 设备未处于可检测到模式,因此无法接收连接。
如果您是要发起与远程设备的连接,则无需启用设备可检测性。仅当您希望应用托管用于接受传入连接的服务器套接字时,才有必要启用可检测性,因为远程设备必须能够发现其他设备,然后才能发起与这些设备的连接。