要进行蓝牙通信需要完成下面四个步骤:
- 设置蓝牙;
- 发现已经配对或者可用的附近的蓝牙设备;
- 连接设备;
- 在不同设备之间传输数据;
本文主要介绍如何使用Bluetooth APIs来完成上述四个步骤。
所有可用的Bluetooth APIs都包含在android.bluetooth包里。下面是创建蓝牙连接的类的总览:
BluetoothAdapter
- 代表本地的蓝牙适配器( local Bluetooth adapter) (Bluetooth radio). BluetoothAdapter是所有蓝牙通信的入口点。使用BluetoothAdapter,你能够探测其他蓝牙设备,获得一个bonded(已配对)的设备列表,使用一个知名的(know)MAC地址实例化一个BluetoothDevice,并创建一个BluetoothServerSocket来监听其他设备的通信。
- 代表一个远程蓝牙设备,使用BluetoothSocket对另一个远程设备发出连接请求,或者查询该远程设备的名字、地址、类和连接状态。
- 代表一个蓝牙socket的接口(类似于TCP socket)。这是应用程序通过InputStream或者OutputStream与其他蓝牙设备交换数据的连接点。
- 表示一个开放的服务器socket,监听进入的连接请求(类似于TCP的ServerSocket)。为了连接两个Android设备,其中一个必须打开一个server socket。当一个远程蓝牙设备发出一个连接请求并被接受时,BluetoothServerSocket将返回一个已连接的BluetoothSocket。
- 描述了一个蓝牙设备的普通特性和能力,它提供了一系列描述一个设备的主要和次要设备类别和服务的只读属性。尽管这个类并不总是可靠地描述一个设备所有的蓝牙profile(配置)和所支持的服务,但它作为设备类型的一个提示是不错的
BluetoothDevice
BluetoothSocket
BluetoothServerSocket
BluetoothClass
为了在应用程序中使用蓝牙功能,我们至少需要声明两方面的权限:BLUETOOTH和BLUETOOTH_ADMIN。
你必须请求BLUETOOTH权限才能够实现蓝牙通信,例如请求一个连接、接受一个连接和传输数据。
你必须请求BLUETOOTH_ADMIN权限,才能够初始化device discovery或者管理蓝牙设置(Bluetooth settings)。大多数应用程序必须具有这个权限才能够发现本地蓝牙设备,这个权限保护的其他能力(除了发现本地设备)不应该被使用,除非你的应用程序是在用户请求的时候能够修改蓝牙设置的管理者。
注意:如果你想要使用BLUETOOTH_ADMIN权限,那么你首先必须有BLUETOOTH权限。
你需要在应用程序的manifest文件中声明程序的蓝牙权限。例如:
- <manifest ... >
- <uses-permission android:name="android.permission.BLUETOOTH" />
- ...
- </manifest>
关于声明应用程序权限的信息,请看<uses-permission>参考。
- 获得BluetoothAdapter类
任何蓝牙activity都需要BluetoothAdapter类。使用静态方法getDefaultAdapter()获得一个BluetoothAdapter的实例,这代表了设备本身的蓝牙适配器(the Bluetooth radio)。整个系统只有一个蓝牙适配器,你的程序可以通过获取到BluetoothAdapter实例与之交互。如果getDefaultAdapter()方法返回null则说明你的设备不支持蓝牙。
- 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);}
- 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()); }}
- // 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 BroadcastReceiverIntentFilter
- filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
- registerReceiver(mReceiver, filter);
- // Don't forget to unregister during onDestroy
- Intent discoverableIntent = newIntent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
- discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
- startActivity(discoverableIntent);
在你的应用程序使用蓝牙进行通信之前,你需要确认你的设备支持蓝牙,如果支持,那么确认它已被启动。
如果你的设备不支持蓝牙,那么你应该关闭任何蓝牙特性。如果蓝牙被支持,那么你可以在你的程序中要求用户启动蓝牙。这需要两个步骤,并且要使用BluetoothAdapter这个类。
如果你的设备不支持蓝牙,那么要关闭任何蓝牙功能。如果支持蓝牙但没有启动,则你可以在程序中要求用户启动蓝牙。启动蓝牙需要两个步骤,并且需要BluetoothAdapter类。
示例代码如下:
2.启动蓝牙
接下来,你必须确保用户启动了蓝牙。调用isEnabled()方法来检查当前蓝牙是否启动。如果该方法返回false,那么说明蓝牙没有启动。这时需要使用“ACTION_REQUEST_ENABLE”action Intent作为参数,调用startActivityForResult()方法来请求启动蓝牙。这将通过系统设备来发出启动蓝牙的请求(不会停止你的程序)。例如:
执行如上的代码将会弹出一个对话框,请求启动蓝牙的用户权限,如Figure 1所示。如果用户点击“Yes”按钮,那么系统将开始启动蓝牙,启动蓝牙(有可能失败)之后你的程序将重新获得焦点。
如果启动蓝牙成功,你的Activity将在onActivityResult()回调函数中接收到一个RESULT_OK结果码。如果蓝牙启动失败或者用户不允许启动蓝牙,则会收到RESULT_CANCELED。
可选的,你的程序能够监听ACTION_STATE_CHANGED broadcast Intent,这个广播Intent是系统探测到蓝牙状态改变时发出的。这个广播Intent包含了EXTRA_STATE和EXTRA_PREVIOUS_STATE两个域,包含了蓝牙旧的和新的状态。这两个域的值可能是STATE_TURNING_ON,STATE_ON,STATE_TURNING_OFF和STATE_OFF。
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.
使用BluetoothAdapter,你能够通过设备发现(device discovery)或者通过查询配对设备的列表来发现远程蓝牙设备。
设备发现(Device discovery)是搜查本地启动蓝牙的设备,然后请求该设备一些信息的一个扫描过程(有时,这被称为“discovering”,“inquiring”或者“scannig”)。但是,本地蓝牙设备只有在启动蓝牙的时候才会对发现请求作出响应。如果一个设备被发现,它将通过共享一些信息,如设备名称、类别和唯一的MAC地址,来对发现请求作出响应。使用这些信息,执行设备发现请求动作的设备就能够初始化一个连接,对被发现的设备发出连接请求。
如果一个远程设备第一次请求连接,那么接收到连接请求的设备会自动发送一个配对请求。如果一个设备已经被配对,那么关于该设备的基本信息(设备名称、类别和MAC地址)将会被保存,并且能用Bluetooth APIs读取。知道了一个远程设备的MAC地址之后,就可以使用该MAC地址在任何时间初始化一个连接,无需再执行device discovery(假设该设备在距离范围之内)。
在被配对和被连接之间是有区别的。被配对意味着两个设备彼此知道对方的存在,有一个连接key被用于认证,能够建立一条加密的连接。被连接意味着设备当前共享一个RFCOMM渠道并且能够传输数据给对方。Android Bluetooth APIs要求设备在建立RFCOMM连接之前要先配对。配对是在你使用Bluetooth APIs建立一个加密连接的时候自动执行的。
下面的章节将描述发现已经配对的设备,或者,使用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.
查询配对设备
在执行device discovery之前,最好在已配对的设备列表中查看所要发现的设备是否已经存在。通过调用getBondedDevices()函数可以获得代表已经配对的设备的BluetoothDevice集合。 例如,你可以查询所有已经配对的设备,然后通过一个ArrayAdapter添加和显示每个设备的名字给用户:
为了建立一个连接,需要才能够BluetoothDevice对象中获取的是MAC地址。在这个例子中,MAC地址作为显示给用户的ArrayAdapter的一部分存储。只要有需要,可以把MAC地址提取出来。
发现设备
调用startDiscovery()开始设备发现的过程,这个过程是异步的,startDiscovery()方法会立即返回一个boolean的值表示启动是否成功。这个发现过程通常包括大约12秒的查询扫描,之后是在发现的设备中查询其蓝牙名称。
你的应用程序中必须注册一个ACTION_FOUND Intent的BroadcastReceiver,用于接收发现一个蓝牙设备时发出的信息。对于每一个设备,系统将广播ACTION_FOUND的Intent。这个Intent包含了一些附加数据域——EXTRA_DEVICE和EXTRA_CLASS,分别包含BluetoothDevice类和BluetoothClass类的实例。
下面代码展示了如何注册设备发现时的广播处理函数:
为了初始化一个连接,我们需要从BluetoothDevice对象中获取MAC地址。
注意: 执行设备发现这个过程,需要花费蓝牙适配器大量资源,是一个重量级过程。如果你发现一个设备并要连接它,最好先调用cancelDiscovery()方法来停止设备发现过程。如果你已经有一个连接,那么执行设备发现过程或导致连接的带宽大幅度减少,所以当你已经有连接的时候最好就不要执行设备发现过程了。
启动发现功能
如果你想要你的设备能被其他设备发现,调用startActivityForResult(Intent,int),传递一个ACTION_REQUEST_DISCOVERABLE action Intent给它。这将发送一个请求给系统设置以启动可被发现模式。可被发现模式一般默认持续120秒,你可以通过给Intent添加一个EXTRA_DISCOVERABLE_DURATION Intent extra来更改可被发现模式的持续时间,这个时间最大是300秒。
请看代码示例:
Figure 2: The enabling discoverability dialog.
一个对话框将会出现,请求用户权限来启动设备的可被发现模式,如Figure 2所示。如果用户点击“Yes”,那么设备在设定的时间内将是可被发现的。你的Activity将调用onActivityResult()回调函数。如果用户点击“No”,那么将产生一个错误,结果码将是Activity.RESULT_CANCELLED。
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
and EXTRA_PREVIOUS_SCAN_MODE
, which tell you the new and old scan mode, respectively. Possible values for each are SCAN_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.