Android BLE 蓝牙实践
前言
最近需要在产品中集成一个蓝牙门锁的功能,之前没有接触过蓝牙(包括经典蓝牙),所以没有头绪,只能硬着头皮找资料,开始挖坑填坑的日子,这篇文章内容是个人经过学习和实践以后总结的,有不对地方请大家指正。
BLE简介
BLE是Bluetooth low energy的意思,属于蓝牙低功耗协议。顾名思义,比一般蓝牙省电,听说有效距离在100米以上,而且一颗纽扣电池就可以工作数年,这些都是网上查到的,在这里再啰嗦一遍。
那么BLE设备是如何工作呢,BLE设备里面包含了Service(服务),Characteristic(特征),Descriptor(描述),一个BLE设备包含一个或多个Service,一个Service包含一个或多个Characteristic,一个Characteristic包含一个value和一个或多个Descriptor,一个Descriptor包含一个value,与BLE设备的通信就是通过与这些个属性打交道。
Android 中的BLE
Android文档中写到:安卓4.3(API 18)为BLE的核心功能提供平台支持和API,App可以利用它来发现设备、查询服务和读写特性。
下面我们来看看Android提供了哪些API给我们使用:
BluetoothManager:从名字可以看出是用来管理蓝牙设备的,用它来获取BluetoothAdapter;
BluetoothAdapter:蓝牙适配器,针对蓝牙模块的操作,比如:关闭,打开,扫描等;
LeScanCallback:在蓝牙扫描过程中,发现一个设备,就会回调一次;
BluetoothDevice:代表一个远程的蓝牙设备
BluetoothGatt:代表一个Bluetooth GATT Profile,用来控制蓝牙开关,和特征的读写等;
BluetoothGattCallback:这个可就厉害了,针对蓝牙的连接状态,读写等操作,这里都会有相应的回调;
BluetoothGattService:表示一个服务;
BluetoothGattCharacteristic:表示一个特征;
BluetoothGattDescriptor:表示一个描述;
以上就是我们开发BLE所需要的API了,下面我们就动手来使用这些API。
实战
启用/关闭蓝牙
/*** 初始化蓝牙适配器*/
public void init(){//获取蓝牙适配器BluetoothManager manager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);mBluetoothAdapter = manager.getAdapter();
}/*** 打开蓝牙*/
public void open() {if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {//不提示直接打开//mBluetoothAdapter.enable();//提示是否打开Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);mContext.startActivity(intent);}
}/*** 关闭蓝牙*/
public void close() {if (mBluetoothAdapter != null) {mBluetoothAdapter.disable();}
}
打开蓝牙有2种方式,一种直接打开,一种是询问用户是否打开,在测试过程中,发现有些机型(例如三星S4)即使使用第一种方式打开,也同样会询问,所以使用了第二种方式,这也是google官方推荐的打开方式。
扫描设备
/*** 扫描设备,定义10秒后自动停止*/
public void scan(){if(mBluetoothAdapter !=null){mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mBluetoothAdapter.stopLeScan(mScanCallback);}},10000);mBluetoothAdapter.startLeScan(mScanCallback);}
}/*** 扫描回调接口*/
private LeScanCallback mScanCallback = new LeScanCallback() {@Overridepublic void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {Log.d("ble",device.getName()+"_"+ device.getAddress());}
};
连接/断开设备
当扫描到设备以后,选择一个设备进行连接:
/*** 连接设备*/
public void connect(BluetoothDevice device) {if (device != null) {mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);}
}private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {if (newState == BluetoothProfile.STATE_CONNECTED) {Log.d("ble","设备已连接");//链接成功发现服务gatt.discoverServices();}else if(newState== BluetoothProfile.STATE_DISCONNECTED){Log.d("ble","设备已断开");}}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicRead(gatt, characteristic, status);Log.d("ble","读取特征值回调");}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);Log.d("ble","特征值改变回调");}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);Log.d("ble","发现服务回调");}
};/*** 断开设备*/
public void disConnect() {if (mBluetoothGatt != null) {mBluetoothGatt.disconnect();}
}
连接上蓝牙设备之后,在开始和蓝牙设备交互之前要先发现服务,否则我们是无法获取服务、特征和描述的,只需要调用BluetoothGatt.discoverServices()
方法即可。
开始通信
与蓝牙进行通信,首先需要知道蓝牙Service的UUID,Characteristic的UUID,Characteristic的属性又分为可读,可写,通知等,可以通过BluetoothGattCharacteristic.getProperties()
判断,这些UUID应该都是由硬件商提供,否则程序无法与蓝牙通信。下面开始贴代码:
/*** 接收特征值 在 {@link #mGattCallback} 的 onCharacteristicChanged 回调** @param sUUID 服务UUID* @param cUUID 特征UUID* @param dUUID 描述UUID* @param enable 是否启用* @return true成功,false失败*/
public boolean receiveCharacteristic(String sUUID, String cUUID, String dUUID, boolean enable) {if (mBluetoothGatt != null) {BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(sUUID));if (service != null) {BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(cUUID));mBluetoothGatt.setCharacteristicNotification(characteristic, enable);if (enable) { //监听需要设置描述BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(dUUID));if (descriptor != null) {descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);if (mBluetoothGatt.writeDescriptor(descriptor)) {return true;} else {return false;}} else {return false;}}return true;} else {return false;}} else {return false;}
}/*** 读取特征值数据 在{@link #mGattCallback}中的 onCharacteristicRead 回到获取结果** @param sUUID 服务UUID* @param cUUID 特征UUID* @return true成功,false失败*/
public boolean readCharacteristic(String sUUID, String cUUID) {if (mBluetoothGatt != null) {BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(sUUID));if (service != null) {BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(cUUID));if (characteristic != null) {return mBluetoothGatt.readCharacteristic(characteristic);} else {return false;}} else {return false;}} else {return false;}
}/*** 写入特征值** @param sUUID 服务UUID* @param cUUID 特征UUID* @param data 写入值* @return true成功,false失败*/
public boolean writeCharacteristic(String sUUID, String cUUID, byte[] data) {if (mBluetoothGatt != null) {BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(sUUID));if (service != null) {BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(cUUID));if (characteristic != null) {return writeValue(characteristic, data);} else {return false;}} else {return false;}} else {return false;}
}private boolean writeValue(BluetoothGattCharacteristic characteristic, byte[] data) {//写入值boolean ret = false;if (data == null) {return false;}int length = data.length;if (length <= dataBufferSize) {characteristic.setValue(data);ret = mBluetoothGatt.writeCharacteristic(characteristic);} else {int count = 0;int offset = 0;while (offset < length) {if ((length - offset) < dataBufferSize) {count = length - offset;} else {count = dataBufferSize;}byte tempArray[] = new byte[count];System.arraycopy(data, offset, tempArray, 0, count);characteristic.setValue(data);ret = mBluetoothGatt.writeCharacteristic(characteristic);if (!ret) {return ret;}offset = offset + count;}}return ret;
}
蓝牙的监听和读取都是异步操作,BluetoothGattCallback 中有很多方法可以重写实现回调,有兴趣的可以试试。
总结
在监听特征值的时候,除了需要服务和特征的UUID,android还需要描述的UUID,往描述里写入值后才能监听到,我做IOS的同事则说IOS不需要描述的UUID,不知道为何,期待大神给出答案。
另外:本文中读取特征值的代码没有实际测试过,因为手中的硬件没有相应的特征,有条件的可以测试一下,如果错误请指正。
最后,完整代码如下:
package com.elf.demo.bluetooth;import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAdapter.LeScanCallback;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.util.Log;import java.util.UUID;/*** ble帮助类* Created by Lidong on 2016/6/5.*/
@TargetApi(18)
public class BLEHelper {private static final int dataBufferSize = 20;protected Context mContext;protected BluetoothAdapter mBluetoothAdapter;protected BluetoothGatt mBluetoothGatt;private Handler mHandler;public BLEHelper(Context context) {mContext = context;mHandler = new Handler();}/*** 初始化蓝牙适配器*/public void init() {//获取蓝牙适配器BluetoothManager manager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);mBluetoothAdapter = manager.getAdapter();}/*** 打开蓝牙*/public void open() {if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {//不提示直接打开//mBluetoothAdapter.enable();//提示是否打开Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);mContext.startActivity(intent);}}/*** 关闭蓝牙*/public void close() {if (mBluetoothAdapter != null) {mBluetoothAdapter.disable();}}/*** 扫描设备,定义10秒后自动停止*/public void scan() {if (mBluetoothAdapter != null) {mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mBluetoothAdapter.stopLeScan(mScanCallback);}}, 10000);mBluetoothAdapter.startLeScan(mScanCallback);}}/*** 扫描回调接口*/private LeScanCallback mScanCallback = new LeScanCallback() {@Overridepublic void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {Log.d("ble", device.getName() + "_" + device.getAddress());}};/*** 连接设备*/public void connect(BluetoothDevice device) {if (device != null) {mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);}}private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {if (newState == BluetoothProfile.STATE_CONNECTED) {Log.d("ble", "设备已连接");//链接成功发现服务gatt.discoverServices();} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {Log.d("ble", "设备已断开");}}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicRead(gatt, characteristic, status);Log.d("ble", "读取特征值回调");}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);Log.d("ble", "特征值改变回调");}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);Log.d("ble", "发现服务回调");}};/*** 断开设备*/public void disConnect() {if (mBluetoothGatt != null) {mBluetoothGatt.disconnect();}}/*** 接收特征值 在 {@link #mGattCallback} 的 onCharacteristicChanged 回调** @param sUUID 服务UUID* @param cUUID 特征UUID* @param dUUID 描述UUID* @param enable 是否启用* @return true成功,false失败*/public boolean receiveCharacteristic(String sUUID, String cUUID, String dUUID, boolean enable) {if (mBluetoothGatt != null) {BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(sUUID));if (service != null) {BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(cUUID));mBluetoothGatt.setCharacteristicNotification(characteristic, enable);if (enable) { //监听需要设置描述BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(dUUID));if (descriptor != null) {descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);if (mBluetoothGatt.writeDescriptor(descriptor)) {return true;} else {return false;}} else {return false;}}return true;} else {return false;}} else {return false;}}/*** 读取特征值数据 在{@link #mGattCallback}中的 onCharacteristicRead 回到获取结果** @param sUUID 服务UUID* @param cUUID 特征UUID* @return true成功,false失败*/public boolean readCharacteristic(String sUUID, String cUUID) {if (mBluetoothGatt != null) {BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(sUUID));if (service != null) {BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(cUUID));if (characteristic != null) {return mBluetoothGatt.readCharacteristic(characteristic);} else {return false;}} else {return false;}} else {return false;}}/*** 写入特征值** @param sUUID 服务UUID* @param cUUID 特征UUID* @param data 写入值* @return true成功,false失败*/public boolean writeCharacteristic(String sUUID, String cUUID, byte[] data) {if (mBluetoothGatt != null) {BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(sUUID));if (service != null) {BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(cUUID));if (characteristic != null) {return writeValue(characteristic, data);} else {return false;}} else {return false;}} else {return false;}}private boolean writeValue(BluetoothGattCharacteristic characteristic, byte[] data) {//写入值boolean ret = false;if (data == null) {return false;}int length = data.length;if (length <= dataBufferSize) {characteristic.setValue(data);ret = mBluetoothGatt.writeCharacteristic(characteristic);} else {int count = 0;int offset = 0;while (offset < length) {if ((length - offset) < dataBufferSize) {count = length - offset;} else {count = dataBufferSize;}byte tempArray[] = new byte[count];System.arraycopy(data, offset, tempArray, 0, count);characteristic.setValue(data);ret = mBluetoothGatt.writeCharacteristic(characteristic);if (!ret) {return ret;}offset = offset + count;}}return ret;}
}
Android BLE 蓝牙实践相关推荐
- Android BLE蓝牙详细解读
代码地址如下: http://www.demodashi.com/demo/15062.html 随着物联网时代的到来,越来越多的智能硬件设备开始流行起来,比如智能手环.心率检测仪.以及各式各样的智能 ...
- android蓝牙设置特征属性,Android BLE蓝牙详细解读(二)
上篇文章主要介绍了关于BLE的一些基础操作,我们能够大概了解到蓝牙操作的一些流程,上文末介绍了本人的一个BLE开源库,支持蓝牙一对多的连接方式,该库封装了蓝牙的开启.扫描.连接.断开.连接超时...一 ...
- Android BLE蓝牙踩坑总结
简介 自从Android-BLE库开源了一段时间以来,越来越多的小伙伴问到了各种各样的关于BLE的奇怪问题,在这里我想跟大家分享一下本人对于Android BLE蓝牙的一些看法和解决方式,避免刚接触的 ...
- Android BLE蓝牙开发知识总结
Android BLE蓝牙开发知识总结 1.蓝牙介绍 1.1什么是蓝牙? 蓝牙( Bluetooth® ):是一种无线技术标准,可实现固定设备.移动设备和楼宇个人域网之间的短距离数据交换(使用2 ...
- Android Ble蓝牙开发总结
Android Ble蓝牙开发总结 前言 本文总结了ble的搜索,连接,读写操作.以及在开发过程中可能遇到的坑. 首先我们需要知道,什么是ble. 蓝牙发展至今经历了8个版本的更新.1.1.1.2.2 ...
- android ble蓝牙接收不到数据_Android蓝牙4.0 Ble读写数据详解 -2
Android蓝牙4.0 Ble读写数据详解 -2 上一篇说了如何扫描与链接蓝牙 这篇文章讲讲与蓝牙的数据传输,与一些踩到的坑. 先介绍一款调试工具,专门调试Ble蓝牙的app.名字叫:nRF-Con ...
- Android BLE 蓝牙开发指南(三)外围设备端开发详解
Android BLE开发指南(一)入门基础 Android BLE开发指南(二)中心设备端程序开发详解 这篇文章将会详细讲解低功耗蓝牙外围设备端程序开发的主要流程.对于Android开发者而言,或许 ...
- Android Ble蓝牙开发
BLE Android 应用 开发 1.权限设置 2.获取蓝牙设备管理器 3.设备搜索 3.1 停止搜索 4.设备连接 5.设备的重连 6.设备的断开与服务关闭 7.通知的注册与接收 8.数据的主动读 ...
- Android BLE 蓝牙开发-扫码枪集成
一.蓝牙模式HID与BLE 当扫码枪与手机连接时,通常采用的是蓝牙HID(Human Interface Device)模式.本质上是一个把扫码枪作为一个硬件键盘,按照键盘协议把扫码后的结果逐个输入到 ...
最新文章
- leetcode-376 摆动序列
- 字节跳动:基于H.266/VVC的移动平台8K超高清实时解码实践 | QCon
- 史上最烂的项目:苦撑 12 年,600 多万行代码!
- python 生成器 迭代器 yiled
- Objetive-C枚举位移操作Swift枚举位移操作
- SmartNIC/DPU — 应用场景与功能特性
- Windows XP任务管理器内进程名详解
- 无风险对冲组合的设计
- uvalive5798(树状数组)
- kotlin读取sd卡里的文件_Kotlin 读取文件
- 体积最小桌面linux,Tiny Core Linux - 体积最小的精简 Linux 操作系统发行版之一 (仅10多MB) - 蓝月网络...
- tokudb 分形树_分形树Fractal tree介绍——具体如何结合TokuDB还没有太懂,先记住其和LSM都是一样的适合写密集...
- java if else程序,java – if then else条件评估
- MySQL 死锁专题问题处理
- VBA学习笔记(6)--抽取第一列中叫“虹虹”的个人信息
- 夏昕.深入浅出Hibernate中的第一个例子体会.
- 怎么自己做淘宝优惠券的网站?看完这个你就全懂了
- 阿里云产品之数据中台架构
- 怎么更换电脑的默认浏览器?
- MyBatisPuls入门案例