Android 蓝牙和BLE应用开发经验参考
传统蓝牙和BLE的区别
技术规范 | 经典蓝牙(2.1 &3.0) | 低功耗蓝牙(4.0) |
无线电频率 | 2.4GHz | 2.4GHz |
距离 | 10米/100米 | 30米 |
数据速率 | 1-3Mbps | 1Mbps |
应用吞吐量 | 0.7-2.1Mbps | 0.2Mbps |
发送数据的总时间 | 100ms | <6ms |
耗电量 | 1 | 0.01至0.5 |
最大操作电流 | <30mA | <15mA(最高运行时为15 mA) |
主要用途 | 手机,游戏机,耳机,立体声音频流,汽车和PC等 | 手机,游戏机,PC,表,体育和健身,医疗保健,汽车,家用电子,自动化和工业等 |
蓝牙的应用开发参考
蓝牙基础
l 蓝牙是一种无线技术标准,可实现固定设备、移动设备和楼宇个人域网之间的短距离数据交换(使用2.4—2.485GHz的ISM波段的UHF无线电波)。蓝牙技术最初由电信巨头爱立信公司于1994年创制,当时是作为RS232数据线的替代方案。蓝牙可连接多个设备,克服了数据同步的难题。
l 如今蓝牙由蓝牙技术联盟(Bluetooth SpecialInterest Group,简称SIG)管理
l 所有的蓝牙标准版本都支持向下兼容
l 蓝牙最新的版本号是蓝牙4.2,还有未发布的蓝牙5.0
l 一个蓝牙主设备最能多和7个蓝牙从设备进行通信
l 蓝牙的数据传输速率在1Mbps以内
l 蓝牙的理论最大通信距离是100米,传输距离越大,功耗越高,现在市面上的流行蓝牙设备的传输距离大约在30米以内,传输距离在10米左右的蓝牙设备最多。
l 蓝牙使用的频率是2.4GHZ
蓝牙应用场景
l 蓝牙应用在手机上
l 蓝牙应用在PC上,现在很多PC都带有蓝牙模块
l 蓝牙应用于其它数字设备,如数字照相机、数字摄像机等
l 篮牙技术构成的电子钱包和电子锁,这方面现在慢慢被主流的NFC所替代
l 篮牙技术在嵌入式设备上的应用如蓝牙音箱,蓝牙耳机,微波炉、洗衣机、电冰箱、空调机等
蓝牙开发基础
常用的蓝牙术语
l 扫描
l 绑定
l 配对
l 连接
蓝牙的通讯模型
蓝牙和普通的网络通信一样,是基于socket进行通讯的,采用的是典型的C/S模型
常用的蓝牙协议(Profile)
Profile名称 | 主要用途 |
SPP即Serial Port Profile串口通讯协议 | 主要用于蓝牙基础数据流的传输 |
A2DP即Advanced Audio Distribution Profile 蓝牙音频传输模型协议 | 主要用来播放高品质的音乐,主要应用场景是蓝牙音箱,蓝牙耳机 |
AVRCP即Audio Video Remote Control Profile音频/视频远程控制协议 | 主要用于蓝牙设备的远程控件,比如控制蓝牙耳机的播放,暂停,继续等 |
HFP即Hands Free Profile 免提协议 | 主要用于车载蓝牙中,可以实现免提功能,可以控制电话的接听,挂断,拒接,音频拨号等 |
HDP即Health Device Profile 健康设备协议 | 主要用于蓝牙血压计,蓝牙体重称等 |
OPP即Object Push Profile 对象推送协议 | 主要用于手机与手机或者手机与电脑之间通过蓝牙进行文件操作,比如通过蓝牙发送文件 |
蓝牙开发核心类
类名 | 解释 |
BluetoothAdapter | 蓝牙适配器类 |
BluetoothDevice | 蓝牙设备信息类 |
BluetoothSocket | 蓝牙客户端Socket类 |
BluetoothServerSocket | 蓝牙服务端Socket类 |
BluetoothHeadset | 蓝牙HFP协议支持类 |
BluetoothA2dp | 蓝牙A2DP协议支持类 |
BluetoothHealth | 蓝牙HDP协议支持类 |
蓝牙开发示例
public static String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";//SPP Profile UUID,这个UUID是蓝牙SIG组织定义的官方ID
BluetoothSocketclientSocket = null;
BluetoothServerSocket serverSocket = null;
InputStreamis;
OutputStream os;
Android蓝牙API历史
Google从Android 3.0开始提供传统蓝牙相关的开发API,蓝牙API在android.bluetooth包下面
蓝牙权限声明
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>
打开蓝牙
通过代码打开
BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter(); if(adapter.isEnabled()) { adapter.disable();//关闭设备蓝牙 } else{ adapter.enable();//打开设备蓝牙 }
通过系统蓝牙对话框打开
public static void openBluetoothWithDialog(Activity activity, int seconds) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
activity.startActivityForResult(intent, REQUEST_ENABLE);
}
注册蓝牙状态监听器
public void registerBluetoothReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_FOUND); filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); // filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); // filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); mActivity.registerReceiver(receiver, filter); } private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); deviceList.add(device); createBond(device); } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(device.getBondState()==BluetoothDevice.BOND_BONDED){ try { clientSocket = BluetoothHelper.connect(device); remoteDevice = clientSocket.getRemoteDevice(); showToast("绑定成功"); is = clientSocket.getInputStream(); os = clientSocket.getOutputStream(); sendMessage("hello boy"); startMessageLoop(); } catch (Exception e) { e.printStackTrace(); } } } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED .equals(action)) { showToast("扫描完成"); } } };
建立蓝牙客户端Socket
public static BluetoothSocket createBluetoothSocket(BluetoothDevice device) {
BluetoothSocket socket = null;
UUID uuid = UUID.fromString(SPP_UUID);
try {
// Method m =device.getClass().getMethod("createRfcommSocket", newClass[]{int.class});
// socket = (BluetoothSocket)m.invoke(device, Integer.valueOf(1));
socket =device.createRfcommSocketToServiceRecord(uuid);
} catch (Exception e) {
try {
socket =device.createInsecureRfcommSocketToServiceRecord(uuid);
} catch (Exception ex) {
socket = null;
}
}
return socket;
}
建立蓝牙服务端Socket
public static BluetoothServerSocket createServerSocket() { BluetoothServerSocket socket = null; UUID uuid = UUID.fromString(SPP_UUID); BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter(); String name=adapter.getName(); try { // Method listenMethod = adapter.getClass().getMethod("listenUsingRfcommOn", new Class[]{int.class}); // socket = ( BluetoothServerSocket) listenMethod.invoke(adapter, new Object[]{ 29}); socket = adapter.listenUsingRfcommWithServiceRecord(name, uuid); } catch (Exception e) { e.printStackTrace(); } return socket; }
蓝牙扫描
BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
if(adapter.isDiscovering())
{
adapter.cancelDiscovery();//取消扫描
}
else{
adapter.startDiscovery();//开始扫描
}
绑定蓝牙设备
public static boolean createBond(BluetoothDevice device) {
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
return device.createBond();
// Method createBondMethod = null;
// try {
// createBondMethod =BluetoothDevice.class.getMethod("createBond");
// Object objResult = createBondMethod.invoke(device);
// if (objResult != null)
// returnBoolean.parseBoolean(objResult.toString());
// return false;
// } catch (Exception e) {
// e.printStackTrace();
// return false;
// }
}
return true;
}
获取系统蓝牙绑定的设备列表
public static ArrayList<BluetoothDevice> getBondedDevices()
{
ArrayList<BluetoothDevice>deviceList=new ArrayList<BluetoothDevice>();
BluetoothAdapter bluetoothAdapter =BluetoothAdapter.getDefaultAdapter();
Object[] devices =bluetoothAdapter.getBondedDevices().toArray();
for (int i = 0; i < devices.length; i++) {
BluetoothDevice device =(BluetoothDevice) devices[i];
if(!deviceList.contains(device))
{
deviceList.add(device);
}
}
return deviceList;
}
判断蓝牙A2DP的连接状态
public static boolean isA2DPDeviceConnected(Context context)
{
BluetoothAdapteradapter=BluetoothAdapter.getDefaultAdapter();
int state=adapter.getProfileConnectionState(BluetoothProfile.A2DP);
return state==BluetoothProfile.STATE_CONNECTED;
}
蓝牙连接
public static BluetoothSocket connect(BluetoothDevice device) {
BluetoothSocket socket = null;
try {
socket = createBluetoothSocket(device);
if (socket == null) return null;
socket.connect();
} catch (Exception e) {
socket = null;
e.printStackTrace();
}
return socket;
}
建立服务端消息循环
serverSocket =createServerSocket();
private void startServerListening() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
clientSocket = serverSocket.accept();
is = clientSocket.getInputStream();
os = clientSocket.getOutputStream();
startMessageLoop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
建立客户端消息循环
private void startMessageLoop() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
byte[] buffer = new byte[1024];
try {
int readed = is.read(buffer);
String msg = new String(buffer, 0, readed);
Stringresult=HexHelper.encodeHexStr(buffer);
if(TextUtils.isEmpty(result)) return;
if(result.equalsIgnoreCase("aa12040042")){
//do something
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
发送蓝牙消息
//aa12040042
byte[]cmd_get_pid=new byte[]
{
(byte)0xaa,
(byte)0x12,
(byte)0x04,
(byte)0x00,
(byte)0x42
};
os.write(cmd_get_pid);
os.flush();
接收蓝牙消息
int readed = is.read(buffer);
String msg = new String(buffer, 0,readed);
String result=HexHelper.encodeHexStr(buffer);
if(TextUtils.isEmpty(result)) return;
if(result.equalsIgnoreCase("aa12040042")){
//do something
}
BLE的应用开发参考
BLE基础
l BLE技术是低成本、短距离、可互操作的鲁棒性无线技术,工作在免许可的2.4GHz ISM射频频段。它从一开始就设计为超低功耗(ULP)无线技术。它利用许多智能手段最大限度地降低功耗。蓝牙低功耗技术采用可变连接时间间隔,这个间隔根据具体应用可以设置为几毫秒到几秒不等。另外,因为BLE技术采用非常快速的连接方式,因此平时可以处于“非连接”状态(节省能源),此时链路两端相互间只是知晓对方,只有在必要时才开启链路,然后在尽可能短的时间内关闭链路。
l BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备
l 低功耗
l 传输距离短
l 传输速度慢
BLE应用场景
l 智能穿戴设备,比如智能手环,智能手表等
l 智能家居,智能灯,智能空调,蓝牙电子称等
l 健康设备,心率监测仪等
BLE开发基础
BLE调试工具
BLE Scanner
BLE Explorer
拼接完整的蓝牙设备UUID
public static final String BASE_ADDRESS="00000000-0000-1000-8000-00805F9B34FB"; //蓝牙UUID的基地址
如上图,假如我们要拼接DeviceInformation Service服务的完整的UUID,我们可以拿蓝牙UUID的基地址的首段+0x180A,
所以我们得到的完整的DeviceInformation Service的UUID是0000180A-0000-1000-8000-00805F9B34FB,我们可以把这个拼接后的UUID用在我们的代码中
BLE服务,特性,特性扫描器的UUID拼接也是这个道理
常用的BLE术语
l GATT
l 服务
l 特性
l 特性描述符
l 中央设备
l 外围设备
关键术语和概念
l Generic Attribute Profile(GATT)—GATT配置文件是一个通用规范,用于在BLE链路上发送和接收被称为“属性”的数据块。目前所有的BLE应用都基于GATT。 蓝牙SIG规定了许多低功耗设备的配置文件。配置文件是设备如何在特定的应用程序中工作的规格说明。注意一个设备可以实现多个配置文件。例如,一个设备可能包括心率监测仪和电量检测。
l Attribute Protocol(ATT)—GATT在ATT协议基础上建立,也被称为GATT/ATT。ATT对在BLE设备上运行进行了优化,为此,它使用了尽可能少的字节。每个属性通过一个唯一的的统一标识符(UUID)来标识,每个String类型UUID使用128 bit标准格式。属性通过ATT被格式化为characteristics和services。
l Characteristic 一个characteristic包括一个单一变量和0-n个用来描述characteristic变量的descriptor,characteristic可以被认为是一个类型,类似于类。
l Descriptor Descriptor用来描述characteristic变量的属性。例如,一个descriptor可以规定一个可读的描述,或者一个characteristic变量可接受的范围,或者一个characteristic变量特定的测量单位。
l Service service是characteristic的集合。例如,你可能有一个叫“Heart Rate Monitor(心率监测仪)”的service,它包括了很多characteristics,如“heart rate measurement(心率测量)”等。你可以在bluetooth.org 找到一个目前支持的基于GATT的配置文件和服务列表。
角色和责任
l 中央 VS 外围设备。 适用于BLE连接本身。中央设备扫描,寻找广播;外围设备发出广播。
l GATT 服务端 VS GATT 客户端。决定了两个设备在建立连接后如何互相交流。
BLE核心类
类名 | 解释 |
BluetoothAdapter | 蓝牙适配器类 |
BluetoothDevice | 蓝牙设备类 |
BluetoothGatt | 作为中央设备来使用和处理数据,是BLE的核心工具类,可以通过这个类来获取BLE设备提供的服务 |
BluetoothGattService | BLE服务类 |
BluetoothGattCharacteristic | BLE特性类 |
BluetoothGattDescriptor | BLE特性描述器类 |
BluetoothGattCallback | BLE核心通讯回调类 |
LeScanCallback | BLE扫描回调类 |
BLE核心类关系图
BLE开发示例
List<BluetoothGattService> mGattServiceList=null; BluetoothDevice mRemoteDevice; BluetoothGatt mBluetoothGatt;
public static final String DEVICE_INFO_SERVICE="00001800-0000-1000-8000-00805F9B34FB"; public static final String SYSTEM_ID_CHAR="00002a04-0000-1000-8000-00805F9B34FB";
BLE开发API历史
Google从Android 4.3开始提供BLE的开发API,所以BLE开发只支持Android 4.3以上的手机
蓝牙权限声明
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
扫描BLE设备
BLE设备扫描回调
LeScanCallback leScanCallback=new LeScanCallback() { /** * BLE设备扫描结果回调函数 * @param device 扫描到的BLE设备 * @param rssi BLE设备的信号强度 * @param scanRecord BLE设备发出的特定信息,可能通过这个信息来判断扫描到的BLE设备是不是指定类型的BLE设备 */ @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { mActivity.runOnUiThread(new Runnable() { @Override public void run() { if(!mDeviceList.contains(device)) { mDeviceList.add(device); mAdapter.notifyDataSetChanged(); } } }); } };
开始BLE设备扫描
public static void startLeScan(UUID[] serviceUuids,LeScanCallback callback) { BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter(); if(serviceUuids==null) { adapter.startLeScan(callback); } else { adapter.startLeScan(serviceUuids, callback); } }
停止BLE设备扫描
public static void stopLeScan(LeScanCallback callback)
{
BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
adapter.stopLeScan(callback);
}
判断特性的属性
判断特性是否可读
public static boolean isBluetoothGattCharacteristicRead(BluetoothGattCharacteristic characteristic) { return (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ)== BluetoothGattCharacteristic.PROPERTY_READ; }
判断特性是否可写
public static boolean isBluetoothGattCharacteristicRead(BluetoothGattCharacteristic characteristic) { return (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ)== BluetoothGattCharacteristic.PROPERTY_READ; }
判断特性是否是Notify特性
public static boolean isBluetoothGattCharacteristicNotify(BluetoothGattCharacteristic characteristic) { return (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY; }
连接BLE设备
和BLE设备通讯的回调
BluetoothGattCallback mGattCallback=new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if(newState==BluetoothGatt.STATE_CONNECTED) { boolean success=gatt.discoverServices();//查找BLE设备提供的所有服务 showToast(gatt.getDevice().getName()+" connected"); } else if(newState==BluetoothGatt.STATE_DISCONNECTED) { if(mGattServiceList!=null) mGattServiceList.clear(); showToast(gatt.getDevice().getName()+" disconnected"); } super.onConnectionStateChange(gatt, status, newState); } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if(status==BluetoothGatt.GATT_SUCCESS) { mGattServiceList=gatt.getServices();//获取BLE设备提供的所有服务列表 BluetoothGattService devInfoService=gatt.getService(UUID.fromString(DEVICE_INFO_SERVICE)); BluetoothGattCharacteristic systemID=devInfoService.getCharacteristic(UUID.fromString(SYSTEM_ID_CHAR)); gatt.readCharacteristic(systemID); } super.onServicesDiscovered(gatt, status); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { try { byte[]value=characteristic.getValue(); if(value!=null) { String str=HexHelper.encodeHexStr(value); Log.i("hello", str); } } catch(Exception e) { Log.i("hello", e.getMessage()); } super.onCharacteristicChanged(gatt, characteristic); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if(status==BluetoothGatt.GATT_SUCCESS) { try { byte[]value=characteristic.getValue(); if(value!=null) { String str=HexHelper.encodeHexStr(value); Log.i("hello", str); } } catch(Exception e) { Log.i("hello", e.getMessage()); } } super.onCharacteristicRead(gatt, characteristic, status); } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); } };
异步连接BLE设备
public void connectGatt(BluetoothDevice device) { mBluetoothGatt=device.connectGatt(App.Instance, false, mGattCallback); }
断开和BLE设备的连接
public void disconnect() { if (mBluetoothGatt != null) { mBluetoothGatt.disconnect(); mBluetoothGatt.close(); mBluetoothGatt=null; } }
设置BLE通知特性
public void setCharacteristicNotification(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, UUID uuid, boolean enable) { if (gatt == null || characteristic==null || uuid==null) return; gatt.setCharacteristicNotification(characteristic, enable); BluetoothGattDescriptor descriptor = characteristic.getDescriptor(uuid); descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); gatt.writeDescriptor(descriptor); }