Android蓝牙开发 — 经典蓝牙&BLE蓝牙

Posted Chin_style

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android蓝牙开发 — 经典蓝牙&BLE蓝牙相关的知识,希望对你有一定的参考价值。

一,前期基础知识储备

1)蓝牙是一种支持设备之间短距离通信的无线电技术(其他还包括红外,WIFI);

支持移动电话、笔记本电脑、无线耳机等设备之间进行信息的交换;

Android支持的蓝牙协议栈:Bluz,BlueDroid,BLE

  • Bluz是Linux推出的,目前使用最广泛;
  • BlueDroid是android4.0之后推出来的,简化了Bluz的操作;
  • BLE是最新的低功耗协议,传输效率和传输速率都是很高的;

2)蓝牙开发技术一共分为两种:

  • 经典蓝牙,3.0版本以下的蓝牙,功耗高,传输数据量大,有效距离10米;
  • 低功耗蓝牙BLE,4.0及以上版本,低功耗,数据量小,有效距离40米;

经典蓝牙的开发包括蓝牙的互相连接、读取蓝牙列表、文件传输、蓝牙耳机等等;

特点:基于Socket连接,传输速率快;缺点:耗电,距离短;
通信的流程,发现设备 -> 配对/绑定设备  ->  建立连接  ->  数据通信  ->  断开连接

BLE蓝牙开发主要是低功耗设备(临近设备间传输少量数据),比如血糖仪、蓝牙手环、蓝牙手表、蓝牙温度枪等等;Android 4.3(API 级别 18)为发挥核心作用的蓝牙低功耗 (BLE) 引入内置平台支持,并提供相应 API,方便应用发现设备、查询服务和传输信息。与传统蓝牙不同,蓝牙低功耗 (BLE) 旨在提供显著降低的功耗这使 Android 应用可与功率要求更严格的 BLE 设备(例如近程传感器、心率监测仪和健身设备)通信

注意:当用户使用 BLE 将其设备与其他设备配对时,用户设备上的所有应用都可以访问在这两个设备间传输的数据。

因此,如果您的应用捕获敏感数据,您应实现应用层安全以保护此类数据的私密性。

3)蓝牙通信底层原理:

Android 平台包含蓝牙网络堆栈支持,此支持能让设备以无线方式与其他蓝牙设备交换数据。应用框架提供通过 Android Bluetooth API 访问蓝牙功能的权限。这些 API 允许应用以无线方式连接到其他蓝牙设备,从而实现点到点和多点无线功能。

为了让支持蓝牙的设备能够在彼此之间传输数据,它们必须先通过配对过程形成通信通道。其中一台设备(可检测到的设备)需将自身设置为可接收传入的连接请求。另一台设备会使用服务发现过程找到此可检测到的设备。在可检测到的设备接受配对请求后,这两台设备会完成绑定过程,并在此期间交换安全密钥。二者会缓存这些密钥,以供日后使用。完成配对和绑定过程后,两台设备会交换信息。当会话完成时,发起配对请求的设备会发布已将其链接到可检测设备的通道。但是,这两台设备仍保持绑定状态,因此在未来的会话期间,只要二者在彼此的范围内且均未移除绑定,便可自动重新连接。

4)关键类和接口

android.bluetooth 包中提供所有 Bluetooth API。以下概要列出了创建蓝牙连接所需的类和接口:

 BluetoothAdapter
表示本地蓝牙适配器(蓝牙无线装置)。BluetoothAdapter 是所有蓝牙交互的入口点。借助该类,您可以发现其他蓝牙设备、查询已绑定(已配对)设备的列表、使用已知的 MAC 地址实例化 BluetoothDevice,以及通过创建 BluetoothServerSocket 侦听来自其他设备的通信。

<!--蓝牙连接权限-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!--蓝牙通讯权限-->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

// 启动蓝牙
public void turnOnBlueTooth(Activity activity, int requestCode) 
	Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
	activity.startActivityForResult(intent, requestCode);
	// mAdapter.enable(); // 谷歌不推荐这种方式


// 关闭蓝牙
public void turnOffBluetooth() 
	mAdapter.disable();


// 打开蓝牙可见性
public void enableVisibily(Context context) 
	Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
	intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
	context.startActivity(intent);

BluetoothDevice
表示远程蓝牙设备。借助该类,您可以通过 BluetoothSocket 请求与某个远程设备建立连接,或查询有关该设备的信息,例如设备的名称、地址、类和绑定状态等。

// 查找设备
public void findDevice() 
	assert (mAdapter != null);
	mAdapter.startDiscovery();


// 绑定设备
public boolean createBond(BluetoothDevice device) 
	boolean result = device.createBond();
	return result;


// 绑定状态
BluetoothDevice.BOND_BONDED
BluetoothDevice.BOND_BONDING
BluetoothDevice.BOND_NONE

// 获取已绑定的蓝牙设备
public List<BluetoothDevice> getBondedDeviceList() 
	return new ArrayList<>(mAdapter.getBondedDevices());


// 解除绑定
public boolean removeBond(Class btClass, BluetoothDevice btDevice)
throws Exception 
	Method removeBondMethod = btClass.getMethod("removeBond");
	Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
	return returnValue.booleanValue();



// 蓝牙操作中发出的广播
private void registerBluetoothReceiver() 
	IntentFilter filter = new IntentFilter();
	//开始查找
	filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
	//结束查找
	filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
	//查找设备
	filter.addAction(BluetoothDevice.ACTION_FOUND);
	//设备扫描可见改变 当我可以被看见时就会发送一个广播过来
	filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
	//绑定状态
	filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);

	registerReceiver(receiver, filter);

BluetoothSocket
表示蓝牙套接字接口(类似于 TCP Socket)。这是允许应用使用 InputStream 和 OutputStream 与其他蓝牙设备交换数据的连接点。

close(),关闭
connect()连接
getInptuStream()获取输入流
getOutputStream()获取输出流
getRemoteDevice()获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备

BluetoothServerSocket
表示用于侦听传入请求的开放服务器套接字(类似于 TCP ServerSocket)。如要连接两台 Android 设备,其中一台设备必须使用此类开放一个服务器套接字。当远程蓝牙设备向此设备发出连接请求时,该设备接受连接,然后返回已连接的 BluetoothSocket。

void    close()
    closes the object and release any system resources it holds.
void    connect()
    attempt to connect to a remote device.
InputStream getInputStream()
    get the input stream associated with this socket.
OutputStream    getOutputStream()
    get the output stream associated with this socket.
BluetoothDevice getRemoteDevice()
    get the remote device this socket is connecting, or connected, to.
获取远程设备,该套接字连接,或连接到---。
boolean isConnected()
    get the connection status of this socket, ie, whether there is an active connection with remote device.
判断当前的连接状态

BluetoothSocket 和 BluetoothServerSocket 
类似于Java中的套接字的 Socket 和 ServerSocket;
在服务器端和客户端进行数据传输的时候都要使用这个类;

服务器端 : 使用BluetoothServerSocket对象可以创建一个BluetoothSocket对象, 调用BluetoothServerSocket的accept()方法就可以获取该对象;
客户端 : 调用BluetoothDevice的createRfcommSocketToServiceRecord()可以获取该对象; 

在服务器端BluetoothServerSocket进行accept()阻塞, 在客户端BluetoothSocket调用connect()连接服务器, 如果连接成功, 
服务器端的accept()方法就会返回BluetoothSocket对象, 同时客户端的BluetoothSocket也成功连接服务器, 
此时服务器端和客户端的BluetoothSocket对象就可以获取输入输出流, 对数据进行操作;


基于蓝牙这套通信流程,可以实现一个简单的聊天程序。

BluetoothHeadset
提供蓝牙耳机支持,以便与手机配合使用。这包括蓝牙耳机配置文件和免提 (v1.5) 配置文件。
BluetoothA2dp
定义如何使用蓝牙立体声音频传输配置文件 (A2DP),通过蓝牙连接将高质量音频从一个设备流式传输至另一个设备

  • A2DP蓝牙立体声音频传输配置文件 (A2DP) 定义如何通过蓝牙连接和流式传输,将高质量音频从一个设备传输至另一个设备。Android 提供 BluetoothA2dp 类,该类是用于控制蓝牙 A2DP 服务的代理。

官方文档:《蓝牙概览》《蓝牙低功耗概览》《BluetoothA2dp

5)电话音频协议(HSP,HFP)和媒体音频协议(A2DP,AVRCP)

  • HSP(手机规格)– 提供手机(移动电话)与耳机之间通信所需的基本功能。
  • HFP(免提规格)– 在 HSP 的基础上增加了某些扩展功能,原来只用于从固定车载免提装置来控制移动电话。
  • A2DP(高级音频传送规格)– 允许传输立体声音频信号。 (相比用于 HSP 和 HFP 的单声道加密,质量要好得多)。
  • AVRCP(音频/视频遥控规格)–用于从控制器(如立体声耳机)向目标设备(如装有 Media Player 的电脑)发送命令(如前跳、暂停和播放)。

关于A2DP,安卓手机都是支持A2DP的,只需要通过广播就可以获取状态了。

关于AVRCP,这部分嵌入式工程师的逻辑多,硬件上的按键可以控制手机app的,比如按键加减时,可以与app交互。

A2DP全名是Advanced Audio Distribution Profile,高质量音频数据传输的协议,其定义里了传送单声道或立体声等高质量音频(区别于蓝牙SCO链路上传输的普通语音)信息的协议和过程。A2DP的典型应用是将音乐播放器的音频数据发送到耳机或音箱。 

参考文章:《Android 蓝牙开发之A2DP基本功能

参考项目:sample-bluetooth-audio(Bluetooth A2DP sample using Android Things)

二,上代码,具体实现

经典蓝牙开发文章:《Android蓝牙开发—经典蓝牙详细开发流程》《android 经典蓝牙开发

BLE蓝牙开发文章:《Android之低功耗蓝牙的基本使用》《Android Kotlin&BLE(低功耗蓝牙) 笔记

                                《Android BLE蓝牙详细解读》《Android 蓝牙开发(三) -- 低功耗蓝牙开发

总结:

1)写过经典蓝牙的就知道,如果说两者的搜索操作还差不多的话,连接操作和写入操作就是完全不同的东西了。

经典蓝牙可以获取到一个类似 TCP 中 Socket 的对象,然后获取 InputStream 和OutputStream,二者分别通过套接字以及 getInputStream()和 getOutputStream()来处理数据传输。

而 BLE 中需要通过不同的 UUID 获取对应的服务、特征才可以写入数据。

2)UUID:每个服务和特征都会有唯一的 UUID ,由硬件决定。
服务(Service):蓝牙设备中可以定义多个服务,相当于功能的集合。
特征(Characteristic):一个服务可以包含多个特征,可以通过 UUID 获取到对应的特征的实例,通过这个实例就可以向蓝牙设备发送 / 读取数据。

蓝牙开源框架:《一款适用经典蓝牙的快速开发框架》《开源蓝牙框架 Android-BLE

三,蓝牙5.0

古老无线再升级 深入了解蓝牙5.0技术

蓝牙5.0是由蓝牙技术联盟在2016年提出的蓝牙技术标准,蓝牙5.0针对低功耗设备速度有相应提升和优化,蓝牙5.0结合wifi对室内位置进行辅助定位,提高传输速度,增加有效工作距离。上一次蓝牙4.2是公布于2014年12月。

补充一点内容

划时代的蓝牙 4.0 

蓝牙技术联盟(Bluetooth SIG)在2010年发布了跨时代的蓝牙4.0,它并不是蓝牙3.0的简单升级版本,
而是全新的技术架构,蓝牙4.0版本分两种模式:单模蓝牙和双模蓝牙。常见的蓝牙音箱,是典型的双模蓝牙,它需要传输大量的音频数据。
而小米手环,蓝牙温度计则属于单模蓝牙。行业里一般不讲单模蓝牙,而是统一称为低功耗蓝牙。

相对于经典蓝牙,低功耗蓝牙芯片有传输远、功耗低、延迟低等优势。传输距离方面,经典蓝牙只有10-100米,而BLE最远能传输300米;
连接方式上,经典蓝牙只能通过点对点的方式传输,而BLE设备能够能通过点对点、广播、Mesh组网与其他设备相连;
在功耗上两者的差别巨大,低功耗蓝牙运行和待机功耗极低,使用一颗纽扣电池便能连续工作数月甚至数年之久。

经典蓝牙主要用于大量音频传输的情景,而低功耗蓝牙主要用在非音频数据传输上。
基于这个差距,经典蓝牙和低功耗蓝牙应用场景有所不同。经典蓝牙主要应用在音频传输设备上, 
而低功耗蓝牙主要用在数据传输领域,尤其是以物联网为主的数据传输。
如血糖仪、蓝牙手环、蓝牙手表、蓝牙温度枪、近程传感器、心率监测仪和健身设备等等。

 蓝牙音频传输 - 手机端的音乐如何通过转码的方式传输到蓝牙耳机进行播放。

参考文章:

经典蓝牙与低功耗蓝牙芯片功能性能对比

BLE技术揭秘

说说蓝牙音频常用的编解码格式

Audio/Video Remote Control Profile (AVRCP)

以上是关于Android蓝牙开发 — 经典蓝牙&BLE蓝牙的主要内容,如果未能解决你的问题,请参考以下文章

Android蓝牙开发——经典蓝牙:配对与解除配对 & 实现配对或连接时不弹出配对框

android开发SPP经典蓝牙

Android 经典蓝牙开发(一)

Android蓝牙开发(二)经典蓝牙消息传输实现

Android蓝牙开发—— 经典蓝牙连接方法

经典蓝牙Android开发(通信)