Android蓝牙4.0 Ble读写数据详解 -2

上一篇说了如何扫描与链接蓝牙 这篇文章讲讲与蓝牙的数据传输,与一些踩到的坑。

先介绍一款调试工具,专门调试Ble蓝牙的app。名字叫:nRF-Connect 谷歌应用商店也能下载到。

这里我先连接一个蓝牙设备 贴几个截图。

UUID的话 就相当于钥匙,蓝牙设备当中有通道,那么通道是需要UUID进行匹配的

当连接上设备之后,可以看到UUID的通道 接下来,按照设备厂商提供的文档,找到我们需要的UUID通道

比如说我这里需要的是0x6a的Service通道 然后点开最后一个Service通道查看

展开Service后 可以看到有两个Characteristic通道

我们看Properties属性 一个是NOTIFY 一个是WRITE 也有可能会有READ这个属性的通道

可以拿这个app输出写出指令给蓝牙,在不清楚是蓝牙的问题还是自己的问题的时候,这个工具还是挺好使的。

Notify的话,需要注意这个Descriptors的UUID 这个在注册Notify的时候,需要用到,这里虽然看不全,但是之后可以通过打印得到。

简单说一下这三种属性的通道的用途

WRITE:顾名思义,写的意思,该通道是用来向蓝牙设备写出数据的通道

READ:向蓝牙设备进行读取数据的通道 这个通道有一个坑 后续会详细写上

Notify:该通道需要注册监听,这是一个通知的通道,蓝牙向你传输数据的话,就能直接收到监听。

我这边的话 因为一些原因,所以没有使用READ通道获取数据 只用了Notify通道 当然 也会讲讲怎么使用READ

准备工作

先将UUID管理起来,我这里的话 采用静态常量的形式保存起来了。

public class UUIDManager {

/**

* 服务的UUID

*/

public static final String SERVICE_UUID = "00006a00-0000-1000-8000-00805f9b34fb";

/**

* 订阅通知的UUID

*/

public static final String NOTIFY_UUID = "00006a02-0000-1000-8000-00805f9b34fb";

/**

* 写出数据的UUID

*/

public static final String WRITE_UUID = "00006a02-0000-1000-8000-00805f9b34fb";

/**

* NOTIFY里面的Descriptor UUID

*/

public static final String NOTIFY_DESCRIPTOR = "00002902-0000-1000-8000-00805f9b34fb";

}

处理通知回调接口

蓝牙的数据回调,其实并不是回调到主线程当中,所以如果接收到数据之后,就进行视图操作的话,是会失败的。

所以我打算切换到主线程进行回调,当然,也可以使用EventBus,不过我不喜欢这东西就没去用。

回调接口的话,打算使用list集合存储起来,然后回调到各个需要数据的地方。 创建以下三个类

/**

* Created by Pencilso on 2017/4/20.

* 蓝牙数据回调监听接口

*/

public interface BlueNotifyListener {

public void onNotify(Message notify);

}

/**

* Created by Pencilso on 2017/4/25.

* 处理回调所有接口

*/

public class NotifyThread implements Runnable {

private List listeners;

private Message notify;

@Override

public void run() {

if (notify == null || listeners==null)

return;

try {

Iterator iterator = listeners.iterator();

while (iterator.hasNext()) {

BlueNotifyListener next = iterator.next();

if (next == null)

iterator.remove();

else

next.onNotify(notify);

}

} catch (Exception e) {

e.printStackTrace();

}

}

public void setListeners(List listeners) {

this.listeners = listeners;

}

public void setNotify(Message notify) {

this.notify = notify;

}

}

/**

* Created by Pencilso on 2017/4/26.

* 蓝牙的Code类 用来自定义回调的标识

*/

public class BlueCodeUtils {

/**

* 蓝牙状态 已连接

*/

public static final int BLUETOOTH_STATE_CONNECT = 0x1;

/**

* 蓝牙状态 已断开

*/

public static final int BLUETOOTH_STATE_DISCONNECT = 0x2;

//*******这些只是自定义的code 就不复制太多了

}

编写蓝牙的功能

新建BluetoothBinder类 继承自BluetoothGattCallback

然后把蓝牙的功能模块写在这里面。

主要的功能都在这个类里面,我把这个放到了服务里面,所以在创建BluetoothBinder的时候需要传递一个Service对象

import android.annotation.SuppressLint;

import android.bluetooth.BluetoothAdapter;

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.os.Build;

import android.os.Message;

import android.support.annotation.RequiresApi;

import android.util.Log;

import java.util.List;

import java.util.UUID;

import cc.petnet.trenmotion.Interface.IBluetoothInterface;

import cc.petnet.trenmotion.utils.HandleUtils;

import cc.petnet.trenmotion.utils.LogUtils;

/**

* Created by Pencilso on 2017/4/20.

* 蓝牙操作的Binder

*/

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)

public class BluetoothBinder extends BluetoothGattCallback implements IBluetoothInterface {

private static BluetoothBinder bluetoothBinder;

private final Service bluetoothService;//service服务

private final BluetoothAdapter adapter;//蓝牙的适配器

private List notifyList;//监听的集合

private BluetoothManager bluetoothManager;//蓝牙管理者

private BluetoothGattService gattService;//通道服务

private BluetoothGatt bluetoothGatt;

public static IBluetoothInterface getInstace() {

return bluetoothBinder;

}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)

protected BluetoothBinder(BluetoothService bluetoothService) {

bluetoothBinder = this;

this.bluetoothService = bluetoothService;

bluetoothManager = (BluetoothManager) bluetoothService.getSystemService(Context.BLUETOOTH_SERVICE);

adapter = bluetoothManager.getAdapter();

}

@Override

public void onDestroy() {

bluetoothBinder = null;

}

@Override

public void addNotifyListener(T notifyListener) {

if (notifyListener != null)

notifyList.add(notifyListener);

}

@Override

public void removeNotifyListener(BlueNotifyListener notifyListener) {

if (notifyListener != null)

notifyList.remove(notifyListener);

}

/**

* 广播蓝牙监听消息

* 因为蓝牙发送过来的消息 并不是处于主线程当中的

* 所以如果直接对蓝牙的数据展示视图的话 会展示不了的 这里的话 封装到主线程当中遍历回调数据

*

* @param notify

*/

public void traverseListener(Message notify) {

NotifyThread notifyThread = new NotifyThread();//创建一个遍历线程

notifyThread.setListeners(notifyList);

notifyThread.setNotify(notify);

HandleUtils.getInstace().post(notifyThread);

}

/**

* 系统的蓝牙是否已经打开

*

* @return

*/

@Override

public boolean isEnable() {

return adapter.isEnabled();

}

@Override

public void enableBluetooth(boolean enable) {

if (enable)

adapter.enable();

else

adapter.disable();

}

@SuppressWarnings("deprecation")

@SuppressLint("NewApi")

@Override

public void startScanner(BluetoothAdapter.LeScanCallback callback) {

adapter.startLeScan(callback);

}

@SuppressLint("NewApi")

@SuppressWarnings("deprecation")

@Override

public void stopScanner(BluetoothAdapter.LeScanCallback callback) {

adapter.stopLeScan(callback);

}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)

@Override

public void connectDevices(String address) {

BluetoothDevice remoteDevice = adapter.getRemoteDevice(address);

BluetoothGatt bluetoothGatt = remoteDevice.connectGatt(bluetoothService, false, this);

}

/**

* 蓝牙设备状态的监听

*

* @param gatt

* @param status

* @param newState 蓝牙的状态被改变

*/

@Override

public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {

super.onConnectionStateChange(gatt, status, newState);

Message message = new Message();

switch (newState) {//对蓝牙反馈的状态进行判断

case BluetoothProfile.STATE_CONNECTED://已链接

message.what = BlueCodeUtils.BLUETOOTH_STATE_CONNECT;

gatt.discoverServices();

break;

case BluetoothProfile.STATE_DISCONNECTED://已断开

message.what = BlueCodeUtils.BLUETOOTH_STATE_DISCONNECT;

break;

}

traverseListener(message);

/**

* 这里还有一个需要注意的,比如说蓝牙设备上有一些通道是一些参数之类的信息,比如最常见的版本号。

* 一般这种情况 版本号都是定死在某一个通道上,直接读取,也不需要发送指令的。

* 如果遇到这种情况,一定要在发现服务之后 再去读取这种数据 不要一连接成功就去获取

*/

}

/**

* 发现服务

*

* @param gatt

* @param status

*/

@Override

public void onServicesDiscovered(BluetoothGatt gatt, int status) {

super.onServicesDiscovered(gatt, status);

gattService = gatt.getService(UUID.fromString(UUIDManager.SERVICE_UUID));// 获取到服务的通道

bluetoothGatt = gatt;

//获取到Notify的Characteristic通道 这个根据协议来定 如果设备厂家给的协议不是Notify的话 就不用做以下操作了

BluetoothGattCharacteristic notifyCharacteristic = gattService.getCharacteristic(UUID.fromString(UUIDManager.NOTIFY_UUID));

BluetoothUtils.enableNotification(gatt, true, notifyCharacteristic);//注册Notify通知

}

/**

* 向蓝牙写入数据

*

* @param data

*/

@SuppressLint("NewApi")

@Override

public boolean writeBuletoothData(String data) {

if (bluetoothService == null) {

return false;

}

BluetoothGattCharacteristic writeCharact = gattService.getCharacteristic(UUID.fromString(UUIDManager.WRITE_UUID));

bluetoothGatt.setCharacteristicNotification(writeCharact, true); // 设置监听

// 当数据传递到蓝牙之后

// 会回调BluetoothGattCallback里面的write方法

writeCharact.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);

// 将需要传递的数据 打碎成16进制

writeCharact.setValue(BluetoothUtils.getHexBytes(data));

return bluetoothGatt.writeCharacteristic(writeCharact);

}

/**

* 蓝牙Notify推送过来的数据

*

* @param gatt

* @param characteristic

*/

@Override

public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {

super.onCharacteristicChanged(gatt, characteristic);

//如果推送的是十六进制的数据的写法

String data = BluetoothUtils.bytesToHexString(characteristic.getValue()); // 将字节转化为String字符串

Message message = new Message();

message.what = BlueCodeUtils.BLUETOOTH_PUSH_MESSAGE;

message.obj = data;

traverseListener(message);//回调数据

// String data = characteristic.getStringValue(0); // 使用场景 例如某个通道里面静态的定死了某一个值,就用这种写法获取 直接获取到String类型的数据

}

/**

* 这里有一个坑 一定要注意,如果设备返回数据用的不是Notify的话 一定要注意这个问题

* 这个方法是 向蓝牙设备写出数据成功之后回调的方法,写出成功之后干嘛呢? 主动去蓝牙获取数据,没错,自己主动去READ通道获取蓝牙数据

* 如果用的是Notify的话 不用理会该方法 写出到蓝牙之后 等待Notify的监听 即onCharacteristicChanged方法回调。

*

* @param gatt

* @param characteristic

* @param status

*/

@Override

public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {

super.onCharacteristicWrite(gatt, characteristic, status);

if (status == BluetoothGatt.GATT_SUCCESS) { //写出成功 接下来 该去读取蓝牙设备的数据了

//这里的READUUID 应该是READ通道的UUID 不过我这边的设备没有用到READ通道 所以我这里就注释了 具体使用 视情况而定

// BluetoothGattCharacteristic readCharact = gattService.getCharacteristic(UUID.fromString(READUUID));

// gatt.readCharacteristic(readCharact);

}

}

/**

* 调用读取READ通道后返回的数据回调

* 比如说 在onCharacteristicWrite里面调用 gatt.readCharacteristic(readCharact);之后 会回调该方法

*

* @param gatt

* @param characteristic

* @param status

*/

@Override

public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {

super.onCharacteristicRead(gatt, characteristic, status);

//如果推送的是十六进制的数据的写法

String data = BluetoothUtils.bytesToHexString(characteristic.getValue()); // 将字节转化为String字符串

Message message = new Message();

message.what = BlueCodeUtils.BLUETOOTH_PUSH_MESSAGE;

message.obj = data;

traverseListener(message);//回调数据

// String data = characteristic.getStringValue(0); // 使用场景 例如某个通道里面静态的定死了某一个值,就用这种写法获取 直接获取到String类型的数据

}

}

其实这个BluetoothBinder还定义了一个接口IBluetoothInterface,然后让BluetoothBinder实现,并且将BluetoothBinder设置单利,暴露方法,提供返回IBluetoothInterface对象

import android.bluetooth.BluetoothAdapter;

import cc.petnet.trenmotion.service.bluetooth.BlueNotifyListener;

/**

* Created by Pencilso on 2017/4/20.

*/

public interface IBluetoothInterface {

/**

*

* @param notifyListener 添加监听事件

* @param BlueNotifyListener监听回调接口

*/

void addNotifyListener(T notifyListener);

/**

*

* @param notifyListener 删除监听接口

*/

void removeNotifyListener(BlueNotifyListener notifyListener);

/**

* 系统是否开启了蓝牙

*

* @return

*/

boolean isEnable();

/**

* 打开或者关闭系统蓝牙

*

* @param enable

*/

void enableBluetooth(boolean enable);

/**

* 启动扫描

*

* @param callback

*/

void startScanner(BluetoothAdapter.LeScanCallback callback);

/**

* 停止扫描

*

* @param callback

*/

void stopScanner(BluetoothAdapter.LeScanCallback callback);

void onDestroy();

/**

* 连接设备

*

* @param address

*/

void connectDevices(String address);

/**

* 向蓝牙写出数据

* @param data

* @return

*/

public boolean writeBuletoothData(String data);

}

Handler工具 主要用来切换到主线程当中

public class HandleUtils {

private static Handler handler = null;

public static Handler getInstace() {

if (handler == null)

handler = new Handler();

return handler;

}

}

BluetoothUtils

public class BluetoothUtils {

/**

* 是否开启蓝牙的通知

*

* @param enable

* @param characteristic

* @return

*/

@SuppressLint("NewApi")

public static boolean enableNotification(BluetoothGatt bluetoothGatt, boolean enable, BluetoothGattCharacteristic characteristic) {

if (bluetoothGatt == null || characteristic == null) {

return false;

}

if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable)) {

return false;

}

//获取到Notify当中的Descriptor通道 然后再进行注册

BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUID.fromString(UUIDManager.NOTIFY_DESCRIPTOR));

if (clientConfig == null) {

return false;

}

if (enable) {

clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

} else {

clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);

}

return bluetoothGatt.writeDescriptor(clientConfig);

}

/**

* 将字节 转换为字符串

*

* @param src 需要转换的字节数组

* @return 返回转换完之后的数据

*/

public static String bytesToHexString(byte[] src) {

StringBuilder stringBuilder = new StringBuilder("");

if (src == null || src.length <= 0) {

return null;

}

for (int i = 0; i < src.length; i++) {

int v = src[i] & 0xFF;

String hv = Integer.toHexString(v);

if (hv.length() < 2) {

stringBuilder.append(0);

}

stringBuilder.append(hv);

}

return stringBuilder.toString();

}

/**

* 将字符串转化为16进制的字节

*

* @param message

* 需要被转换的字符

* @return

*/

public static byte[] getHexBytes(String message) {

int len = message.length() / 2;

char[] chars = message.toCharArray();

String[] hexStr = new String[len];

byte[] bytes = new byte[len];

for (int i = 0, j = 0; j < len; i += 2, j++) {

hexStr[j] = "" + chars[i] + chars[i + 1];

bytes[j] = (byte) Integer.parseInt(hexStr[j], 16);

}

return bytes;

}

}

蓝牙基本上到这已经结束了。

android ble蓝牙接收不到数据_Android蓝牙4.0 Ble读写数据详解 -2相关推荐

  1. android java 写文件操作_Android编程之文件的读写实例详解

    本文实例分析了Android编程之文件的读写方法.分享给大家供大家参考,具体如下: Android的文件读写与JavaSE的文件读写相同,都是使用IO流.而且Android使用的正是JavaSE的IO ...

  2. Android自动化测试环境部署及adb sdkmanager avdmanager Monitor DDMS工具使用及命令详解

    环境部署及工具使用 系列文章 前言 环境部署 硬件环境 软件环境 ADB工具 adb组成 adb命令 android命令 sdkmanager 命令 avdmanager命令 管理模拟器 monito ...

  3. 关于android的4.2的0文件夹的详解

    关于android的4.2的0文件夹的详解 ---- android 4.0 ---- 在galaxy nexus(GN)手机上userdata分区很大,被挂在/data目录,用户的数据通常是放在sd ...

  4. android 4.2目录结构,关于android的4.2的0文件夹的详解(目录结构挂载分析)

    关于android的4.2的0文件夹的详解 ---- android 4.0 ---- 在galaxy nexus(GN)手机上userdata分区很大,被挂在/data目录,用户的数据通常是放在sd ...

  5. Android MIFARE读写器详解2

    Android MIFARE读写器详解2 Mifare是NXP公司生产的一系列遵守ISO14443A标准的射频卡,包Mifare S50.Mifare S70.Mifare UltraLight.Mi ...

  6. Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解

    Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解 目录 Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解 一.OpenGL ES渲染管线 1.基本处 ...

  7. pandas数据索引之loc、iloc、ix详解及实例

    pandas数据索引之loc.iloc.ix详解及实例 先来个总结: loc函数:通过行索引 "Index" 中的具体值来取行数据(如取"Index"为&quo ...

  8. 全民大数据时代已来 阿里数加平台详解

    文章讲的是全民大数据时代已来 阿里数加平台详解,业界流行一种说法,云计算与大数据就是一枚硬币的两面,相生相惜,不可分割.在当下互联网时代,数据的价值越来越受到社会的认可,并在今天,已然成为一种普惠资源 ...

  9. ant如何形成时间轴和图库_Python数据可视化常用4大绘图库原理详解_python

    这篇文章主要介绍了Python数据可视化常用4大绘图库原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 今天我们就用一篇文章,带大家梳理mat ...

  10. android-短信验证功能,Android实现获取短信验证码的功能以及自定义GUI短信验证详解...

    <Android实现获取短信验证码的功能以及自定义GUI短信验证详解>由会员分享,可在线阅读,更多相关<Android实现获取短信验证码的功能以及自定义GUI短信验证详解(8页珍藏版 ...

最新文章

  1. 使用 PHP 在站点上构建类似 Twitter 的系统
  2. 挑战弱监督学习的三大热门问题 AutoWSL2019挑战赛正式开赛
  3. python字典之defaultdict详解
  4. 公司Windows Office升级项目总结
  5. React 组件学习
  6. 图像处理实战 多张曲线同图共舞
  7. 【NLP】使用BERT完成NLP任务
  8. 【渝粤教育】 国家开放大学2020年春季 1171科学与技术 参考试题
  9. jquery手写轮播图_jquery 实现轮播图详解及实例代码_jquery_脚本之家
  10. 中国移动与清华大学达成战略合作,将共同研究6G
  11. Android 系统(252)---Android:BLE智能硬件开发详解
  12. GeoServer的安装与数据服务发布
  13. c语言经典100例对考研有用吗,考研数学经验,没用算这学长输
  14. oracle实现批量drop表,ORACLE下批量DROP表格
  15. WPF中Tooltip使用技巧总结
  16. 【赖世雄音标】——元音
  17. Oracle的 alter table table_name nologging; 的使用
  18. 区块链正在颠覆的18个行业
  19. ant的下载与安装(一)
  20. Chapter 19 稳恒磁场

热门文章

  1. 死锁终结者:顺序锁和轮询锁!
  2. 最常见并发面试题整理!(速度收藏)
  3. 面试官:数据量大的情况下分页查询很慢,有什么优化方案?
  4. 计算机组成原理和体系结构----软考(到处copy)
  5. LaTeX的安装教程及问题记录
  6. html的数据类型有哪些,数据库数据类型有哪些
  7. python 3d大数据可视化_基于Python的数据可视化库pyecharts介绍
  8. 单点登录 cas 设置回调地址_单点登录落地实现技术有哪些,有哪些流行的登录方案搭配?...
  9. java变量小明扑克牌_算法练习篇之:扑克牌顺子
  10. 链表 python 牛客_牛客网《剑指offer》之Python2.7实现:合并两个排序的链表