前言

最近需要在产品中集成一个蓝牙门锁的功能,之前没有接触过蓝牙(包括经典蓝牙),所以没有头绪,只能硬着头皮找资料,开始挖坑填坑的日子,这篇文章内容是个人经过学习和实践以后总结的,有不对地方请大家指正。

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 蓝牙实践相关推荐

  1. Android BLE蓝牙详细解读

    代码地址如下: http://www.demodashi.com/demo/15062.html 随着物联网时代的到来,越来越多的智能硬件设备开始流行起来,比如智能手环.心率检测仪.以及各式各样的智能 ...

  2. android蓝牙设置特征属性,Android BLE蓝牙详细解读(二)

    上篇文章主要介绍了关于BLE的一些基础操作,我们能够大概了解到蓝牙操作的一些流程,上文末介绍了本人的一个BLE开源库,支持蓝牙一对多的连接方式,该库封装了蓝牙的开启.扫描.连接.断开.连接超时...一 ...

  3. Android BLE蓝牙踩坑总结

    简介 自从Android-BLE库开源了一段时间以来,越来越多的小伙伴问到了各种各样的关于BLE的奇怪问题,在这里我想跟大家分享一下本人对于Android BLE蓝牙的一些看法和解决方式,避免刚接触的 ...

  4. Android BLE蓝牙开发知识总结

    Android BLE蓝牙开发知识总结 1.蓝牙介绍 1.1什么是蓝牙?    蓝牙( Bluetooth® ):是一种无线技术标准,可实现固定设备.移动设备和楼宇个人域网之间的短距离数据交换(使用2 ...

  5. Android Ble蓝牙开发总结

    Android Ble蓝牙开发总结 前言 本文总结了ble的搜索,连接,读写操作.以及在开发过程中可能遇到的坑. 首先我们需要知道,什么是ble. 蓝牙发展至今经历了8个版本的更新.1.1.1.2.2 ...

  6. android ble蓝牙接收不到数据_Android蓝牙4.0 Ble读写数据详解 -2

    Android蓝牙4.0 Ble读写数据详解 -2 上一篇说了如何扫描与链接蓝牙 这篇文章讲讲与蓝牙的数据传输,与一些踩到的坑. 先介绍一款调试工具,专门调试Ble蓝牙的app.名字叫:nRF-Con ...

  7. Android BLE 蓝牙开发指南(三)外围设备端开发详解

    Android BLE开发指南(一)入门基础 Android BLE开发指南(二)中心设备端程序开发详解 这篇文章将会详细讲解低功耗蓝牙外围设备端程序开发的主要流程.对于Android开发者而言,或许 ...

  8. Android Ble蓝牙开发

    BLE Android 应用 开发 1.权限设置 2.获取蓝牙设备管理器 3.设备搜索 3.1 停止搜索 4.设备连接 5.设备的重连 6.设备的断开与服务关闭 7.通知的注册与接收 8.数据的主动读 ...

  9. Android BLE 蓝牙开发-扫码枪集成

    一.蓝牙模式HID与BLE 当扫码枪与手机连接时,通常采用的是蓝牙HID(Human Interface Device)模式.本质上是一个把扫码枪作为一个硬件键盘,按照键盘协议把扫码后的结果逐个输入到 ...

最新文章

  1. leetcode-376 摆动序列
  2. 字节跳动:基于H.266/VVC的移动平台8K超高清实时解码实践 | QCon
  3. 史上最烂的项目:苦撑 12 年,600 多万行代码!
  4. python 生成器 迭代器 yiled
  5. Objetive-C枚举位移操作Swift枚举位移操作
  6. SmartNIC/DPU — 应用场景与功能特性
  7. Windows XP任务管理器内进程名详解
  8. 无风险对冲组合的设计
  9. uvalive5798(树状数组)
  10. kotlin读取sd卡里的文件_Kotlin 读取文件
  11. 体积最小桌面linux,Tiny Core Linux - 体积最小的精简 Linux 操作系统发行版之一 (仅10多MB) - 蓝月网络...
  12. tokudb 分形树_分形树Fractal tree介绍——具体如何结合TokuDB还没有太懂,先记住其和LSM都是一样的适合写密集...
  13. java if else程序,java – if then else条件评估
  14. MySQL 死锁专题问题处理
  15. VBA学习笔记(6)--抽取第一列中叫“虹虹”的个人信息
  16. 夏昕.深入浅出Hibernate中的第一个例子体会.
  17. 怎么自己做淘宝优惠券的网站?看完这个你就全懂了
  18. 阿里云产品之数据中台架构
  19. 怎么更换电脑的默认浏览器?
  20. MyBatisPuls入门案例

热门文章

  1. 千言数据集:文本相似度——BERT完成NSP任务
  2. VS2010 窗口 混乱 ,窗口排版被搞乱了怎么办?
  3. 宝藏级BI数据可视化功能|图表联动分析
  4. php人才系统 转让,PHP云人才系统 phpyun v3.2 正式版
  5. Django项目部署
  6. 北京嘉楠科技面试总结
  7. 最老程序员创业札记:全文检索、数据挖掘、推荐引擎应用3
  8. 法考主观题计算机答题吗,2019年法考主观题考场发的是电子法条还是纸质法条?...
  9. Android自定义View绘制流程
  10. DSPE-PEG4-propargyl小分子PEG试剂,单分散PEG