蓝牙是一种短距离无线通信技术,它由爱立信公司于1994年创制,原本想替代连接电信设备的数据线,但是后来发现它也能用于移动设备之间的数据传输,所以蓝牙技术在手机上获得了长足发展。
因为手机内部的通讯芯片一般同时集成了2G/3G/4G、WIFI和蓝牙,所以蓝牙功能已经是智能手机的标配了。若想进行蓝牙方面的开发,需要在App工程的AndroidManifest.xml中补充下面的权限配置:
与NFC类似,Android也提供了蓝牙模块的管理工具,名叫BluetoothAdapter,虽然通常把BluetoothAdapter翻译为“蓝牙适配器”,其实它干的是管理器的活。下面是BluetoothAdapter类常用的方法说明:
getDefaultAdapter : 获取默认的蓝牙适配器。该方法为静态方法。
getState : 获取蓝牙的开关状态。STATE_ON表示已开启,STATE_TURNING_ON表示正在开启,STATE_OFF表示已关闭,STATE_TURNING_OFF表示正在关闭。
enable : 启用蓝牙功能。
disable : 禁用蓝牙功能。
isEnabled : 判断蓝牙功能是否启用。返回true表示已启用,返回false表示未启用。
getBondedDevices : 获取已配对的设备集合。
getRemoteDevice : 根据设备地址获取远程的设备对象。
startDiscovery : 开始搜索周围的蓝牙设备。
cancelDiscovery : 取消搜索周围的蓝牙设备。
isDiscovering : 判断是否正在搜索周围的蓝牙设备。
接下来通过一个检测蓝牙设备并配对的例子,介绍如何在App开发中运用蓝牙技术。不要小看这个例子,简简单单的功能可得分成四个步骤:初始化、启用蓝牙、搜索蓝牙设备、与指定设备配对,下面分别进行详细说明:
一、初始化蓝牙适配器
如果仅仅是普通的蓝牙连接,则调用getDefaultAdapter获取蓝牙适配器就行了。初始化蓝牙适配器的代码示例如下:
二、启用蓝牙功能
虽然BluetoothAdapter提供了enable方法用于启用蓝牙功能,但是该方法并不允许外部发现本设备,所以等于没用。实际开发中要弹窗提示用户,是否允许其他设备检测到自身,弹窗代码如下所示:
蓝牙权限的选择对话框如下图所示:
由于选择弹窗上面可选择“允许”还是“拒绝”,因此代码中要重写onActivityResult函数,在该函数中判断蓝牙权限的选择结果。下面是判断权限选择的例子代码:
三、搜索周围的蓝牙设备
蓝牙功能打开之后,就能调用startDiscovery方法去搜索周围的蓝牙设备了。不过因为搜索动作是个异步的过程,startDiscovery方法并不直接返回搜索发现的设备结果,而是通过广播BluetoothDevice.ACTION_FOUND返回新发现的蓝牙设备。所以页面代码需要注册一个蓝牙搜索结果的广播接收器,在接收器中解析蓝牙设备信息,再把新设备添加到蓝牙设备列表。
下面是蓝牙搜索接收器的注册、注销,以及内部逻辑处理的代码例子:
搜索到的蓝牙设备可能会有多个,每发现一个新设备都会收到一次发现广播,这样设备列表是动态刷新的。搜索完成的蓝牙设备列表界面如下图所示,其中左图为A手机的设备列表,右图为B手机的设备列表:
四、与指定的蓝牙设备配对
注意到新发现的设备状态是“未绑定”,这意味着当前手机并不能跟对方设备进行数据交互。只有新设备是“已绑定”状态,才能与当前手机传输数据。蓝牙设备的“未绑定”与“已绑定”,区别在于这两部设备之间是否成功配对了,而配对操作由BluetoothDevice类管理。下面是BluetoothDevice类的常用方法说明:
getName : 获取设备的名称。
getAddress : 获取设备的MAC地址。
getBondState : 获取设备的绑定状态。BOND_NONE表示未绑定,BOND_BONDING表示正在绑定,BOND_BONDED表示已绑定。
createBond : 建立该设备的配对信息。该方法为隐藏方法,需要通过反射调用。
removeBond : 移除该设备的配对信息。该方法为隐藏方法,需要通过反射调用。
从上面的方法说明可以看出,搜索获得新设备后,即可调用设备对象的createBond方法建立配对。但配对成功与否的结果同样不是立即返回的,因为系统会弹出配对确认框供用户选择,就像下面的两个界面截图那样,左图是A手机上的配对弹窗,右图是B手机上的配对弹窗。
只有用户在两部手机都选择了“配对”按钮,才算是双方正式搭配好了。由于配对请求需要在界面上手工确认,因此配对结果只能通过异步机制返回,此处的结果返回仍然采取广播形式,即系统会发出广播BluetoothDevice.ACTION_BOND_STATE_CHANGED通知App。故而前面第三步的广播接收器得增加过滤绑定状态的变更动作,接收器内部也要补充更新蓝牙设备的绑定状态了。修改后的广播接收器相关代码片段如下所示:
两部手机配对完毕,分别刷新自己的设备列表页面,将对方设备的绑定状态改为“已绑定”,然后它俩就可以眉目传情,传递小纸条什么的了。下面是更新状态后的设备列表界面,其中左图为A手机的设备列表,右图为B手机的设备列表:
因为手机内部的通讯芯片一般同时集成了2G/3G/4G、WIFI和蓝牙,所以蓝牙功能已经是智能手机的标配了。若想进行蓝牙方面的开发,需要在App工程的AndroidManifest.xml中补充下面的权限配置:
- <!-- 蓝牙 -->
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <!-- 如果Android6.0 蓝牙搜索不到设备,需要补充下面两个权限 -->
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
getDefaultAdapter : 获取默认的蓝牙适配器。该方法为静态方法。
getState : 获取蓝牙的开关状态。STATE_ON表示已开启,STATE_TURNING_ON表示正在开启,STATE_OFF表示已关闭,STATE_TURNING_OFF表示正在关闭。
enable : 启用蓝牙功能。
disable : 禁用蓝牙功能。
isEnabled : 判断蓝牙功能是否启用。返回true表示已启用,返回false表示未启用。
getBondedDevices : 获取已配对的设备集合。
getRemoteDevice : 根据设备地址获取远程的设备对象。
startDiscovery : 开始搜索周围的蓝牙设备。
cancelDiscovery : 取消搜索周围的蓝牙设备。
isDiscovering : 判断是否正在搜索周围的蓝牙设备。
接下来通过一个检测蓝牙设备并配对的例子,介绍如何在App开发中运用蓝牙技术。不要小看这个例子,简简单单的功能可得分成四个步骤:初始化、启用蓝牙、搜索蓝牙设备、与指定设备配对,下面分别进行详细说明:
一、初始化蓝牙适配器
如果仅仅是普通的蓝牙连接,则调用getDefaultAdapter获取蓝牙适配器就行了。初始化蓝牙适配器的代码示例如下:
- private BluetoothAdapter mBluetooth;
- private void initBluetooth() {
- mBluetooth = BluetoothAdapter.getDefaultAdapter();
- if (mBluetooth == null) {
- Toast.makeText(this, "本机未找到蓝牙功能", Toast.LENGTH_SHORT).show();
- finish();
- }
- }
二、启用蓝牙功能
虽然BluetoothAdapter提供了enable方法用于启用蓝牙功能,但是该方法并不允许外部发现本设备,所以等于没用。实际开发中要弹窗提示用户,是否允许其他设备检测到自身,弹窗代码如下所示:
- // 弹出是否允许扫描蓝牙设备的选择对话框
- Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
- startActivityForResult(intent, mOpenCode);
![](https://i-blog.csdnimg.cn/blog_migrate/e81a706e5932de5655d064eda46f5de6.png)
由于选择弹窗上面可选择“允许”还是“拒绝”,因此代码中要重写onActivityResult函数,在该函数中判断蓝牙权限的选择结果。下面是判断权限选择的例子代码:
- private int mOpenCode = 1;
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
- super.onActivityResult(requestCode, resultCode, intent);
- if (requestCode == mOpenCode) {
- mHandler.postDelayed(mRefresh, 50); // 刷新蓝牙设备列表
- if (resultCode == RESULT_OK) {
- Toast.makeText(this, "允许本地蓝牙被附近的其它蓝牙设备发现", Toast.LENGTH_SHORT).show();
- } else if (resultCode == RESULT_CANCELED) {
- Toast.makeText(this, "不允许蓝牙被附近的其它蓝牙设备发现", Toast.LENGTH_SHORT).show();
- }
- }
- }
三、搜索周围的蓝牙设备
蓝牙功能打开之后,就能调用startDiscovery方法去搜索周围的蓝牙设备了。不过因为搜索动作是个异步的过程,startDiscovery方法并不直接返回搜索发现的设备结果,而是通过广播BluetoothDevice.ACTION_FOUND返回新发现的蓝牙设备。所以页面代码需要注册一个蓝牙搜索结果的广播接收器,在接收器中解析蓝牙设备信息,再把新设备添加到蓝牙设备列表。
下面是蓝牙搜索接收器的注册、注销,以及内部逻辑处理的代码例子:
- private void beginDiscovery() {
- // 如果当前不是正在搜索,则开始新的搜索任务
- if (!mBluetooth.isDiscovering()) {
- mBluetooth.startDiscovery();
- }
- }
- @Override
- protected void onStart() {
- super.onStart();
- //需要过滤多个动作,则调用IntentFilter对象的addAction添加新动作
- IntentFilter discoveryFilter = new IntentFilter();
- discoveryFilter.addAction(BluetoothDevice.ACTION_FOUND);
- //注册搜索结果的接收器
- registerReceiver(discoveryReceiver, discoveryFilter);
- }
- @Override
- protected void onStop() {
- super.onStop();
- // 注销广播接收器
- unregisterReceiver(discoveryReceiver);
- }
- // 蓝牙设备的搜索结果通过广播返回
- private BroadcastReceiver discoveryReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- Log.d(TAG, "onReceive action=" + action);
- // 获得已经搜索到的蓝牙设备
- if (action.equals(BluetoothDevice.ACTION_FOUND)) {
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- refreshDevice(device); // 将发现的蓝牙设备加入到设备列表
- }
- }
- };
![](https://i-blog.csdnimg.cn/blog_migrate/a35284a6faa04a9c4cb3dc2579122551.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f84f98ab064fa9e880366f56f9832bf5.png)
四、与指定的蓝牙设备配对
注意到新发现的设备状态是“未绑定”,这意味着当前手机并不能跟对方设备进行数据交互。只有新设备是“已绑定”状态,才能与当前手机传输数据。蓝牙设备的“未绑定”与“已绑定”,区别在于这两部设备之间是否成功配对了,而配对操作由BluetoothDevice类管理。下面是BluetoothDevice类的常用方法说明:
getName : 获取设备的名称。
getAddress : 获取设备的MAC地址。
getBondState : 获取设备的绑定状态。BOND_NONE表示未绑定,BOND_BONDING表示正在绑定,BOND_BONDED表示已绑定。
createBond : 建立该设备的配对信息。该方法为隐藏方法,需要通过反射调用。
removeBond : 移除该设备的配对信息。该方法为隐藏方法,需要通过反射调用。
从上面的方法说明可以看出,搜索获得新设备后,即可调用设备对象的createBond方法建立配对。但配对成功与否的结果同样不是立即返回的,因为系统会弹出配对确认框供用户选择,就像下面的两个界面截图那样,左图是A手机上的配对弹窗,右图是B手机上的配对弹窗。
![](https://i-blog.csdnimg.cn/blog_migrate/6b70d06a2d1313468124e98baf29ba6c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/13bd9a45ccd3ca1dfd4a645698938bdc.png)
只有用户在两部手机都选择了“配对”按钮,才算是双方正式搭配好了。由于配对请求需要在界面上手工确认,因此配对结果只能通过异步机制返回,此处的结果返回仍然采取广播形式,即系统会发出广播BluetoothDevice.ACTION_BOND_STATE_CHANGED通知App。故而前面第三步的广播接收器得增加过滤绑定状态的变更动作,接收器内部也要补充更新蓝牙设备的绑定状态了。修改后的广播接收器相关代码片段如下所示:
- @Override
- protected void onStart() {
- super.onStart();
- //需要过滤多个动作,则调用IntentFilter对象的addAction添加新动作
- IntentFilter discoveryFilter = new IntentFilter();
- discoveryFilter.addAction(BluetoothDevice.ACTION_FOUND);
- // 增加绑定状态的变更动作
- discoveryFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
- //注册搜索结果的接收器
- registerReceiver(discoveryReceiver, discoveryFilter);
- }
- // 蓝牙设备的搜索结果通过广播返回
- private BroadcastReceiver discoveryReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- Log.d(TAG, "onReceive action=" + action);
- // 获得已经搜索到的蓝牙设备
- if (action.equals(BluetoothDevice.ACTION_FOUND)) {
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- refreshDevice(device); // 将发现的蓝牙设备加入到设备列表
- } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- // 更新蓝牙设备的绑定状态
- if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
- tv_discovery.setText("正在配对" + device.getName());
- } else if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
- tv_discovery.setText("完成配对" + device.getName());
- } else if (device.getBondState() == BluetoothDevice.BOND_NONE) {
- tv_discovery.setText("取消配对" + device.getName());
- }
- }
- }
- };
![](https://i-blog.csdnimg.cn/blog_migrate/de99283cda948f3c63a42595f3881449.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bf294037b8d1d8019315758c9dd985cd.png)