写给android应用开发工程师的蓝牙入门指南

4,992 阅读8分钟

这是我参与更文挑战的第4天,活动详情查看: 更文挑战

6月的更文或许会迟到,但是却不会缺席。这两我们天不刷题,学习一下蓝牙方面的知识。

这周末不做饭,打算更新3篇与蓝牙相关的文章。

  1. 写给Android应用开发工程师的蓝牙使用流程与案例(主要是蓝牙的基础使用流程)
  2. 如何打造一个与车载蓝牙进行联动的音频播放器
  3. 从源码的角度理解蓝牙工作流程

蓝牙由来

蓝牙(Bluetooth)一词取自于十世纪丹麦国王哈拉尔的名字 Harald Bluetooth。据说这位国王酷爱吃蓝莓,所以他的牙龈每天都是蓝色的,因此他的外号就叫做蓝牙王。

可能大家不知道,蓝牙(Bluetooth)一词取自于十世纪丹麦国王哈拉尔的名字 Harald Bluetooth。据说这位国王酷爱吃蓝莓,所以他的牙龈每天都是蓝色的,因此他的外号就叫做蓝牙王。

吃葡萄蓝牙.jpg

而将「蓝牙」与后来的无线通讯技术标准关联在一起的,是一位来自英特尔的工程师 Jim Kardach。

哈拉尔国王以统一了因宗教战争和领土争议而分裂的挪威与丹麦而闻名于世,国王的成就与 Jim Kardach 的理念不谋而合,他希望蓝牙也可以成为统一的通用传输标准——将所有分散的设备与内容互联互通。

现在大家所看到的蓝牙的图标,也是根据蓝牙国王的名字而来的,是由哈拉尔•蓝牙名字的首字母“H”和“B”的小Futhark弗萨克文字(属于老日耳曼如尼文)字母“ᚼ(Hagall)”和“ᛒ(Bjarkan)”拼在一起,成为了今天大家熟知的蓝色徽标(这里有资料说是老如尼文也是没错的,因为小Futhark弗萨克文字就是属于老如尼文)。

蓝牙图标.jpg

蓝牙协议栈组成

在应用层的角度我们可以不用太过深入理解蓝牙的相关协议,但是需要有一个概念性的认识,就像在刚学习开发的时候我们不必深究tcp/ip的每一层细节是如何实现的,只需要通过系统api进行数据传输就可以了。但是有了这些概念我们可以根据自身需求决定是否深入到某些细节进行研究 android蓝牙架构.png

蓝牙开发流程

  1. 声明权限
  2. 蓝牙设置
  3. 发现设备
  4. 连接设备

蓝牙权限声明

蓝牙的使用有三个权限:

BLUETOOTH:需要此权限才能执行任何蓝牙通信,例如请求连接、接受连接和传输数据。

ACCESS_FINE_LOCATION:蓝牙扫描可用于收集用户的位置信息。此类信息可能来自用户自己的设备,以及在商店和交通设施等位置使用的蓝牙信标。

BLUETOOTH_ADMIN:这个权限不是必须,但是如果您想让应用启动设备发现或操纵蓝牙设置,需要使用这个权限。

权限声明代码:

<!--蓝牙权限-->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <!-- If your app targets Android 9 or lower, you can declare
         ACCESS_COARSE_LOCATION instead. -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!--蓝牙权限-->

蓝牙管理

  • 检测设备是否支持蓝牙
  • 启用蓝牙
  • 监听蓝牙状态变化

在正常使用蓝牙功能之前我们需要判断当前设备是否支持蓝牙,如果不支持的话我们就不应该在使用蓝牙相关的功能。如果设备支持蓝牙,但是蓝牙已经关闭那么我们可以在不离开应用的情况下开启蓝牙。这些都需要借助BluetoothAdapter。

参考代码如下:

BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {//当bluetoothAdapter 为空的时候说明应用不支持蓝牙
            Toast.makeText(this, "当前设备不支持蓝牙", Toast.LENGTH_SHORT).show();
            return;
        }

        if (!bluetoothAdapter.isEnabled()) {//蓝牙不可用,开启蓝牙
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

如果成功启用蓝牙,会在 Activity 的 onActivityResult() 回调中收到 RESULT_OK 结果代码。如果由于某个错误(或用户响应“No”)未成功启用蓝牙,则结果代码为 RESULT_CANCELED。

同时我们还可以通过广播注册的方式,来监听蓝牙的状态变化。

蓝牙有四种状态

STATE_TURNING_ON 正在打开

STATE_ON 打开

STATE_TURNING_OFF 正在关闭

STATE_OFF 关闭

每当蓝牙状态发生变化时,系统都会广播此 Intent。此广播包含额外字段 EXTRA_STATE 和 EXTRA_PREVIOUS_STATE,二者分别表示新的和旧的蓝牙状态。其值为上面4中状态中的一种。

IntentFilter intentFilter = new IntentFilter();
        //监听蓝牙状态改变Action
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);

设备发现

  • 发现别的设备
  • 监听发现的设备
  • 能够被别的设备发现
  • 监听被识别状态改变

设备发现是一个扫描过程,它会搜索局部区域内已启用蓝牙功能的设备,并请求与每台设备相关的某些信息。此过程有时也被称为发现、查询或扫描。但是,只有在当下接受信息请求时,附近区域的蓝牙设备才会通过启用可检测性响应发现请求。如果设备已启用可检测性,它会通过共享一些信息(例如设备的名称、类及其唯一的 MAC 地址)来响应发现请求。借助此类信息,执行发现过程的设备可选择发起对已检测到设备的连接。

在首次与远程设备建立连接后,系统会自动向用户显示配对请求。当设备完成配对后,系统会保存关于该设备的基本信息(例如设备的名称、类和 MAC 地址),并且可使用 Bluetooth API 读取这些信息。借助远程设备的已知 MAC 地址,您可以随时向其发起连接,而无需执行发现操作(假定该设备仍处于有效范围内)。

请注意,被配对与被连接之间存在区别:

  • 被配对是指两台设备知晓彼此的存在,具有可用于身份验证的共享链路密钥,并且能够与彼此建立加密连接。
  • 被连接是指设备当前共享一个 RFCOMM 通道,并且能够向彼此传输数据。当前的 Android Bluetooth API 要求规定,只有先对设备进行配对,然后才能建立 RFCOMM 连接。在使用 Bluetooth API 发起加密连接时,系统会自动执行配对。

查询配对设备

在执行设备发现之前,您必须查询已配对的设备集,以了解所需的设备是否处于已检测到状态。为此,请调用 getBondedDevices()。此方法会返回一组表示已配对设备的 BluetoothDevice 对象。BluetoothDevice包含了蓝牙连接的相关信息。

Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

蓝牙设备发现

Android系统通过BluetoothAdapter 提供了三个设备发现相关的api

startDiscovery()//开始设备查找
cancelDiscovery()//取消设备查找
isDiscovering()//是否正在执行设备发现

处理发现的设备

要接收处理设备发现信息必须注册一个 action为BluetoothDevice.ACTION_FOUND的广播。系统会为每台设备广播此 Intent。Intent 包含额外字段 EXTRA_DEVICE 和 EXTRA_CLASS,二者又分别包含 BluetoothDevice 和 BluetoothClass。BluetoothDevice包含蓝牙连接的相关信息,BluetoothClass可以查看当前设备支持的协议。

蓝牙设备的可检测性

如果您希望将本地设备设为可被其他设备检测到,请使用 ACTION_REQUEST_DISCOVERABLE Intent 调用 startActivityForResult(Intent, int)。这样便可发出启用系统可检测到模式的请求,从而无需导航至设置应用,避免暂停使用您的应用。默认情况下,设备处于可检测到模式的时间为 120 秒(2 分钟)。通过添加 EXTRA_DISCOVERABLE_DURATION Extra 属性,您可以定义不同的持续时间,最高可达 3600 秒(1 小时)。

示例代码:

Intent discoverableIntent =
        new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);//如果时间设置成0,那么设备会一直处于可检测状态
startActivity(discoverableIntent);

监听设备的可检测性

设备将在分配的时间内以静默方式保持可检测到模式。如果您希望在可检测到模式发生变化时收到通知,则可以为 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 设备未处于可检测到模式,且无法收到连接。 如果您要发起对远程设备的连接,则无需启用设备可检测性。只有当您希望应用对接受传入连接的服务器套接字进行托管时,才有必要启用可检测性,因为在发起对其他设备的连接之前,远程设备必须能够发现这些设备。

需要注意的是 如果尚未在设备上启用蓝牙,则启用设备可检测性会自动启用蓝牙。

蓝牙连接

蓝牙的连接会涉及到 两个关键类BluetoothServerSocket 、BluetoothSocket 。BluetoothServerSocket作为服务端来监听客户端的连接请求,BluetoothSocket用于已经建立的连接发送数据。

参考代码:利用蓝牙进行数据传输。 github.com/xiaolutang/…

运行是要注意,需要两个设备进行配合,一段作为客户端,一端作为服务端来进行连接运行。 项目运行起来后向下滑动找到 android 蓝牙demo点击进入就可以看到

效果截图

SocketDemo界面.png

运行结果

SocketDemo运行结果.png

下一小节预告

打造一个能与车载蓝牙进行互动的音频播放器。敬请期待。

既然都看到了这里,点个赞鼓励下作者吧。打卡不易

文章参考:

www.shenzhenware.com/articles/12…

www.360doc.com/content/16/…

developer.android.google.cn/guide/topic…

www.360doc.com/content/17/…