Android Developer -- Bluetooth篇 开发实例之一 扫描设备
第一步:声明Bluetooth Permissions
<!-- 设置蓝牙访问权限 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
第二步:获取BluetoothAdapter,判断该设备是否支持蓝牙
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter == null) { // Device does not support Bluetooth // 说明该设备不支持蓝牙 }
第三步:检查当前的蓝牙是否开启
if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); // 不做提示,强行打开 // mAdapter.enable(); }
如果是第一种方式:会出现提示弹窗
A dialog will appear requesting user permission to enable Bluetooth, as shown in Figure 1. If the user responds "Yes," the system will begin to enable Bluetooth and focus will return to your application once the process completes (or fails).
If enabling Bluetooth succeeds, your Activity will receive the RESULT_OK
result code in the onActivityResult()
callback. If Bluetooth was not enabled due to an error (or the user responded "No") then the result code will be RESULT_CANCELED
.
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_ENABLE_BT) { if (resultCode == RESULT_OK) { // 蓝牙已经开启 Log.d("h_bl", "蓝牙已经开启完毕"); String bluetoothName = bluetoothAdapter.getName(); // 获取本地蓝牙名称 String bluetoothAddress = bluetoothAdapter.getAddress(); // 获取本地蓝牙地址 tv_bluetoothName.append(bluetoothName); tv_bluetoothAddress.append(bluetoothAddress); } else { Log.d("h_bl", "蓝牙开启失败"); } } super.onActivityResult(requestCode, resultCode, data); }
其中,
private int REQUEST_ENABLE_BT = 1; // 蓝牙打开的请求码
Optionally, your application can also listen for the ACTION_STATE_CHANGED
broadcast Intent, which the system will broadcast whenever the Bluetooth state has changed. This broadcast contains the extra fields EXTRA_STATE
and EXTRA_PREVIOUS_STATE
, containing the new and old Bluetooth states, respectively. Possible values for these extra fields areSTATE_TURNING_ON
, STATE_ON
, STATE_TURNING_OFF
, and STATE_OFF
. Listening for this broadcast can be useful to detect changes made to the Bluetooth state while your app is running.
可选的,你的应用可以监听ACTION_STATE_CHANGED广播intent,当系统蓝牙状态改变将会发起这个广播,这个广播包含了EXTRA_STATE和EXTRA_PREVIOUS_STATE额外的字段,包含了新的和旧的蓝牙状态分别的,可能 有STATE_TURNING_ON,STATE_ON,STATE_TURNING_OFF和STATE_OFF可能的值,监听广播能够 对于检测蓝牙状态改变是有用的。 --- 用来判断蓝牙是否开启完毕
第3.1步:检查当前的蓝牙是否开启完毕
// 蓝牙状态改变的广播 private final BroadcastReceiver bluetoothState = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String stateExtra = BluetoothAdapter.EXTRA_STATE; int state = intent.getIntExtra(stateExtra, -1); switch (state) { case BluetoothAdapter.STATE_TURNING_ON: // 蓝牙打开中 break; case BluetoothAdapter.STATE_ON: // 蓝牙打开完成 break; case BluetoothAdapter.STATE_TURNING_OFF: // 蓝牙关闭中 break; case BluetoothAdapter.STATE_OFF: // 蓝牙关闭完成 break; } } }; // 蓝牙状态改变的广播 IntentFilter filter2 = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(bluetoothState,new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)); // 蓝牙状态改变的广播
Tip: Enabling discoverability will automatically enable Bluetooth. If you plan to consistently enable device discoverability before performing Bluetooth activity, you can skip step 2 above. Read about enabling discoverability, below.
提示:启用程序会自动启用蓝牙。如果你打算开启设备的可见性,可以跳过上面的2步。阅读关于启用可发现,下面。 --- 设置蓝牙的可见性,就把Intent添加值,看第五步
第四步:寻找其他设备
Using the BluetoothAdapter
, you can find remote Bluetooth devices either through device discovery or by querying the list of paired (bonded) devices.
Device discovery is a scanning procedure that searches the local area for Bluetooth enabled devices and then requesting some information about each one (this is sometimes referred to as "discovering," "inquiring" or "scanning"). However, a Bluetooth device within the local area will respond to a discovery request only if it is currently enabled to be discoverable. If a device is discoverable, it will respond to the discovery request by sharing some information, such as the device name, class, and its unique MAC address. Using this information, the device performing discovery can then choose to initiate a connection to the discovered device.
Once a connection is made with a remote device for the first time, a pairing request is automatically presented to the user. When a device is paired, the basic information about that device (such as the device name, class, and MAC address) is saved and can be read using the Bluetooth APIs. Using the known MAC address for a remote device, a connection can be initiated with it at any time without performing discovery (assuming the device is within range).
Remember there is a difference between being paired and being connected. To be paired means that two devices are aware of each other's existence, have a shared link-key that can be used for authentication, and are capable of establishing an encrypted connection with each other. To be connected means that the devices currently share an RFCOMM channel and are able to transmit data with each other. The current Android Bluetooth API's require devices to be paired before an RFCOMM connection can be established. (Pairing is automatically performed when you initiate an encrypted connection with the Bluetooth APIs.)
The following sections describe how to find devices that have been paired, or discover new devices using device discovery.
Note: Android-powered devices are not discoverable by default. A user can make the device discoverable for a limited time through the system settings, or an application can request that the user enable discoverability without leaving the application. How to enable discoverability is discussed below.
使用BluetoothAdapter,你能够打开可见性的设备(搜索到的蓝牙设备)和已经配对的的设备列表。
设备发现是一个扫描程序,搜索本地区具有蓝牙功能的设备,然后请求一些信息(这是有时被称为“发现中”,“查询中”或“扫描中”)。然而,只有局域网内的蓝牙设备处于能够被发现的状态中,才能响应别的设备发送的发现请求。如果一个设备是可以被发现的,它会响应发现请求并共享一些信息,比如设备名称、类别,并以唯一的MAC地址。使用此信息,该设备执行发现,然后选择启动一个初始化连接到设备。
当第一次去连接远程设备的时候,会提交一个配对请求给用户确认。当设备配对时,一些基本的信息会被保存,并可以通过APIs去读取这些信息(such as the device name, class, and MAC address)。使用一个已知的MAC地址去连接设备的时候,无论该设备是否处于发现状态,都可以去连接。(但设备是在范围内)。
记住配对和连接之间的区别。已经配对的意味着两个设备是互相认识对方的存在,已经有了一个用来认证的共享的key,然后可以建立一个加密的连接。而连接是说设备已经建立了RFCOMM通道,并可以交换数据。现在的Android Bluetooth APIs 需要在建立RFCOMM通道之前,需要先配对。(使用Bluetooth APIs初始化加密连接的时候,会自动匹配。)
下面的章节将介绍如何找到已配对的设备,或者使用设备发现发现新设备。
Note: Android设备默认是不可发现的。用户可以通过系统设置使设备能在有限的时间内被发现,或 应用程序可以要求用户在没有离开该应用的时候使蓝牙可见。
4.1 找到已经配对的设备
Before performing device discovery, its worth querying the set of paired devices to see if the desired device is already known. To do so, call getBondedDevices()
. This will return a Set of BluetoothDevice
s representing paired devices. For example, you can query all paired devices and then show the name of each device to the user, using an ArrayAdapter:
在执行“发现设备”之前,先获取到已经配对设备的set是很有值得的。调用getBondedDevices()即可,会返回一组已配对的set。例如,你可以查询已配对的设备,并利用ArrayAdapter去显示它们的名字。
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); // If there are paired devices if (pairedDevices.size() > 0) { // Loop through paired devices for (BluetoothDevice device : pairedDevices) { // Add the name and address to an array adapter to show in a ListView mArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } } else { Toast.makeText(getApplicationContext(), "没有找到已匹对的设备!", Toast.LENGTH_SHORT).show(); }
All that's needed from the BluetoothDevice
object in order to initiate a connection is the MAC address. In this example, it's saved as a part of an ArrayAdapter that's shown to the user. The MAC address can later be extracted in order to initiate the connection. You can learn more about creating a connection in the section about Connecting Devices.
通过BluetoothDevice的MAC地址去初始化连接,这里只是显示给用户看。MAC可以被提取,然后去初始化连接。你可以在 Connecting Devices章节学习到更多创建连接。
4.2 发现设备
调用startDiscovery()函数后,系统将扫描12秒,返回找到的蓝牙设备。我们需用广播来接收。
For each device, the system will broadcast the ACTION_FOUND
Intent. This Intent carries the extra fields EXTRA_DEVICE
and EXTRA_CLASS
, containing a BluetoothDevice
and a BluetoothClass
, respectively. For example, here's how you can register to handle the broadcast when devices are discovered:
每发现一个设备,系统就会发送ACTION_FOUND
Intent.的广播,这个Intent携带额外的字段EXTRA_DEVICE
and EXTRA_CLASS,包含a
BluetoothDevice
and a BluetoothClass。
// Create a BroadcastReceiver for ACTION_FOUND private final BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the BluetoothDevice object from the Intent BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // Add the name and address to an array adapter to show in a ListView mArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } } }; // Register the BroadcastReceiver IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
其中,需要调用
// 扫描蓝牙设备 btAdapter.startDiscovery();
警告:Once you have found a device to connect, be certain that you always stop discovery with cancelDiscovery()
before attempting a connection。
Caution: Performing device discovery is a heavy procedure for the Bluetooth adapter and will consume a lot of its resources. Once you have found a device to connect, be certain that you always stop discovery with cancelDiscovery()
before attempting a connection. Also, if you already hold a connection with a device, then performing discovery can significantly reduce the bandwidth available for the connection, so you should not perform discovery while connected.
连接设备之前,要调用cancelDiscovery(),以便节约资源。并且不要执行连接的时候,去执行发现操作。
另外,需要取消广播:
@Override protected void onDestroy() { unregisterReceiver(mReceiver); super.onDestroy(); }
第五步:设备的可发现时间设定,默认是120S。
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivity(discoverableIntent);
The maximum duration an app can set is 3600 seconds, and a value of 0 means the device is always discoverable. Any value below 0 or above 3600 is automatically set to 120 secs)
一个应用程序可以设置最大持续时间为3600秒,填写 0 的值表示设备总是发现。任何值低于0或高于3600自动设置为120秒。
会提示弹窗
Note: If Bluetooth has not been enabled on the device, then enabling device discoverability will automatically enable Bluetooth.
如果蓝牙尚未启用的设备,并使设备可发现将自动启用蓝牙。
The device will silently remain in discoverable mode for the allotted time. If you would like to be notified when the discoverable mode has changed, you can register a BroadcastReceiver for the ACTION_SCAN_MODE_CHANGED
Intent. This will contain the extra fields EXTRA_SCAN_MODE
andEXTRA_PREVIOUS_SCAN_MODE
, which tell you the new and old scan mode, respectively. Possible values for each areSCAN_MODE_CONNECTABLE_DISCOVERABLE
, SCAN_MODE_CONNECTABLE
, or SCAN_MODE_NONE
, which indicate that the device is either in discoverable mode, not in discoverable mode but still able to receive connections, or not in discoverable mode and unable to receive connections, respectively.
You do not need to enable device discoverability if you will be initiating the connection to a remote device. Enabling discoverability is only necessary when you want your application to host a server socket that will accept incoming connections, because the remote devices must be able to discover the device before it can initiate the connection.
可以接受“发现模式”的改变的广播,会接收到ACTION_SCAN_MODE_CHANGED
Intent,包含额外的字段EXTRA_SCAN_MODE
andEXTRA_PREVIOUS_SCAN_MODE
,描述了新的和旧的扫描模式。其值可能是SCAN_MODE_CONNECTABLE_DISCOVERABLE
, SCAN_MODE_CONNECTABLE
, or SCAN_MODE_NONE。分别代表设备处于在发现模式;不可发现模式但仍能接收连接;不可发现模式和无法接收连接。
你不需要非得使设备可见,如果你初始化去连接到远程设备的时候。当你作为主机 host a server socket,让别的设备连接进来的时候,就必须是处于可见模式!因为远程设备要发现主机,才可以初始化连接。