安卓BLE开发教程(二) BLE开发流程

在安卓上进行BLE开发时,就不必像理解BLE协议栈那样复杂了。因为安卓的BLE包为我们提供了十分丰富的API、各类常量、各类连接通信情况下的回调API等。

具体流程

一、声明权限

二、获取Adapter适配器

三、开启蓝牙

四、BLE扫描与停止

五、连接设备

六、枚举特征值及其属性

七、利用特征值通讯

八、关闭蓝牙


一、声明权限

在AndroidManifest.xml文件中声明应用需要的特性及权限。

<!-- 声明App使用条件为支持BLE -->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<!-- 声明蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!-- 安卓6.0开始需要此权限 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

二、获取Adapter适配器

final BluetoothManager mBluetoothManager =(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();

三、开启蓝牙

if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(intent, BLE_ENABLE);
}

四、BLE扫描与停止

private boolean mScanning;//是否正在搜索
private Handler mHandler = new Handler();
// 预设15秒扫描时间
private static final int SCAN_PERIOD = 15000;private void scanLeDevice(final boolean enable) {if (enable) {// 控制BLE扫描时间mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mBluetoothAdapter.stopLeScan(mLeScanCallback);}}, SCAN_PERIOD);mScanning = true;// 开始扫描mBluetoothAdapter.startLeScan(mLeScanCallback);} else {mScanning = false;// 停止扫描mBluetoothAdapter.stopLeScan(mLeScanCallback);}
}

在BLE扫描回调函数中保存设备对应的BlueToothDevice对象,rssi信号强度等。

private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {@Overridepublic void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {// 保存device及相关信息,也可以通知UI线程显示信息}};

五、连接设备

使用扫描结果中保存的BluetoothDevice对象调用connectGatt进行通讯。

BluetoothGatt mBluetoothGatt;
// 参数一:context上下文 
// 参数二:是否自动重连
// 参数三: 连接回调
mBluetoothGatt = mBluetoothDevice.connectGatt(context, false, mGattCallback);

连接回调函数实现如下,开发时在此处处理各类可能遇到的情况。

private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {// 连接状态改变的回调@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) {// 具体处理见后文第八项};// 发现服务回调@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {// 具体处理见后文第六项};@Overridepublic void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {// 由mBluetoothGatt.readRemoteRssi()调用得到,可不停刷新rssi信号强度Log.e(TAG, "信号强度RSSI:" + rssi);}// 写描述信息回调@Overridepublic void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptordescriptor, int status) {};// 写操作回调@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {Log.e(TAG, "写入成功:" + characteristic.getValue());}};// 读操作回调@Overridepublic void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {Log.e(TAG, "读取成功:" + characteristic.getValue());}}// 数据改变回调(接收BLE设备发送的数据)@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {};};
}

六、枚举特征值及其属性

@Override
// 发现服务回调,即调用了mBluetoothGatt.discoverServices()执行的回调
public void onServicesDiscovered(BluetoothGatt gatt, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {// 获取ServiceList<BluetoothGattService> mGattServices = gatt.getServices();// 获取Service的Characteristicsfor (BluetoothGattService gattService : mGattServices) {List<BluetoothGattCharacteristic> mGattCharacteristics =     gattService.getCharacteristics();for (BluetoothGattCharacteristic gattCharacteristic :mGattCharacteristics) {int charaProp = gattCharacteristic.getProperties();// 所有Characteristics按属性分类if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {Log.e(TAG, "gattCharacteristic的UUID为:" +             gattCharacteristic.getUuid());Log.e(TAG, "gattCharacteristic的属性为:可读");readUuids.add(gattCharacteristic.getUuid());}if ((charaProp | BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {Log.e(TAG, "gattCharacteristic的UUID为:" + gattCharacteristic.getUuid());Log.e(TAG, "gattCharacteristic的属性为:可写");writeUuids.add(gattCharacteristic.getUuid());}if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {Log.e(TAG, "gattCharacteristic的UUID为:" + gattCharacteristic.getUuid() + gattCharacteristic);Log.e(TAG, "gattCharacteristic的属性为:具备通知属性");notifyUuids.add(gattCharacteristic.getUuid());}}}} else {Log.e(TAG, "onServicesDiscovered 失败,status:" + status);}
}

七、利用特征值通讯

1、写数据

public void writeChara(byte) {BluetoothGattCharacteristic mGattCharacteristic =             mBluetoothGatt.getCharacteristic(writeUuid);mGattCharacteristic.setValue(sendValue);mBluetoothGatt.writeCharacteristic(mGattCharacteristic);
}

2、读数据

public void readChara() {// 读取数据BluetoothGattCharacteristic mGattCharacteristic =         mBluetoothGatt.getCharacteristic(readUuid);mBluetoothGatt.readCharacteristic(mGattCharacteristic);
}

3、监听通知属性的数据

mBluetoothGatt.setCharacteristicNotification(mGattCharacteristic, enabled);
BluetoothGattDescriptor mGattDescriptor = mGattCharacteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
mGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(mGattDescriptor);

八、关闭蓝牙

合理的关闭步骤为,先调用BluetoothGatt#disconnect进行断开连接,此时会在BluetoothGattCallback#onConnectionStateChange接收到断开成功的回调,然后在回调中调用BluetoothGatt#close释放相关资源。

// 首先执行该disconnect操作,然后等待回调通知
mBluetoothGatt.disconnect();
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) {if (newState != BluetoothGatt.GATT_SUCCESS) {// 连接失败直接在此处理即可,因此时调用disconnect无法进入下面条件Log.e(TAG, "连接失败, status:" + status);gatt.close();return;}if (newState == BluetoothGatt.STATE_DISCONNECTED) {// 连接断开Log.e(TAG, "连接断开");mBluetoothGatt.close();} else if (newState == BluetoothProfile.STATE_CONNECTED) {// 连接成功后启动服务发现Log.e(TAG, "连接成功");mBluetoothGatt.discoverServices();}
};

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/403033.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux驱动如何在不同版本上快速迭代升级

As well known&#xff0c;Linux内核版本更新很快&#xff0c;有些内核版本的迭代升级可能会导致在使用的驱动版本存在编译失败或使用的兼容性问题&#xff0c;如何快速定位到内核版本间变更的地方&#xff0c;并处理掉该问题&#xff0c;列一下我常用的解决方法。&#xff08;…

Ipad平板作为MAC苹果电脑的扩展屏幕的技术研究

直入主题&#xff0c;这方面的研究直接参考当前使用基数最大&#xff0c;反馈最好的两个产品。最新产品讯息&#xff0c;请分别进入各自官网。BTW&#xff0c;Duet Display也跟随Luna Display开始发布硬件了。 Duet Display 颠覆了基于 Wi-Fi 的传统运作原理&#xff0c;改经…

使用std::thread线程相关函数,-static静态编译的程序运行时的一些常见错误

使用std::thread的应用程序&#xff0c;编译时如果是动态链接pthread线程库运行正常&#xff0c;-static静态链接时在某些平台下可能会遇到一些意外错误。如常见编译命令&#xff1a;g -stdC11 test.c -o test -pthread 1、Segmentation fault&#xff08;段错误&#xff09; …

C++ Tips

1、析构函数调用时机 <1> 栈中定义的对象程序会自动调用析构函数 例如CLassName object;这样声明的对象&#xff0c;当程序运行到了对象作用域之外或者程序退出&#xff0c;对象都会被销毁&#xff0c;当然析构函数也会被调用 <2> 堆中new的对象 使用new声明的…

The futex facility returned an unexpected error code

在 linux 程序执行中若遇到该错误&#xff0c;考虑下是否是如下变量使用了强制内存对齐导致。 比如&#xff1a;在将如上变量包含到结构体中&#xff0c;强制1字节或2字节内存对齐。 如&#xff1a;信号量相关 struct semaphore&#xff0c;线程相关的 pthread_mutex_t&#…

Windows系统USB转CDC串口驱动限制说明

USB转串口芯片目前主流的几种USB类别有&#xff1a; 1、USB 厂商类&#xff1b; 2、USB CDC类&#xff1b; 3、USB HID类&#xff1b; 其中若使用USB CDC系统内置驱动时&#xff0c;使用时会有诸多特殊性&#xff0c;如下为使用说明&#xff1a; 1、测试中出现若打开串口状…

苹果MacOS系统上安装第三方驱动失败/无效

近期不少用户在苹果系统上安装一些第三方驱动时反馈没有作用&#xff0c;但是驱动安装提示是完成的&#xff0c;并拷贝到了系统的驱动路径下&#xff1b;造成该问题的原因可参见如下苹果官方说明&#xff1a; User-Approved Kernel Extension Loading 引用下第一段官网说明 …

gcc工具链查看默认编译选项

命令&#xff1a; echo "" | gcc -v -x c -E - 如在Ubuntu系统下输出结果为&#xff1a; ramboubuntu:/tmp$ echo "" | gcc -v -x c -E - Using built-in specs. COLLECT_GCCgcc OFFLOAD_TARGET_NAMESnvptx-none OFFLOAD_TARGET_DEFAULT1 Target: x86_6…

OpenWrt 之 MT7628 移植第三方SPI驱动

1、在OpenWrt系统上移植SPI驱动前&#xff0c;首先要确保SPI相关引脚未被复用为其他功能&#xff0c;比如GPIO&#xff1b;以下操作已假定该条件成立&#xff0c;否则请修改相关dts和c文件中复用配置&#xff1b; 2、打开dts配置文件进行修改&#xff0c;这里我是用的硬件为WR…

OpenWrt 之 MT7628 使用GPIO中断

在支持设备树的系统中使用中断一般有2种方式。 一、DTS配置interrupt节点 这里有个挺好的博客&#xff0c;链接地址&#xff1a;https://biscuitos.github.io/blog/DTS-interrupt/ 也即&#xff0c;找到dts文件中的GPIO中断控制器节点&#xff0c;然后在你的dts驱动节点中根…

CH9102 USB转串口应用体验

近期使用CH9102 USB转串口芯片成功用在原有使用CP2102的产品板上&#xff0c;整个替换和验证过程还是很顺利的&#xff0c;顺带写个blog做个记录。 原项目上使用CP2102搭载ESP32实现Arduino物联网应用&#xff0c;采用USB转串口芯片实现串口下载&#xff0c;代码的Debug调试&am…

CH9101 USB转串口替换FT232R和FT230XQ

学生党一枚&#xff0c;前段时间跟着导师做的项目因为上面用到USB转串口芯片FT232R迟迟买不到&#xff0c;所以打算更换成国产USB转串口芯片CH340&#xff0c;对CH340的认识也很早了&#xff0c;很多年前开始直到现在各种开发板上基本都会标配一颗CH340&#xff0c;像某宝上的S…

FT230X芯片的国产化替代

之前有些项目用到FT230XQ芯片&#xff0c;无奈不好买&#xff08;价格高&#xff09;&#xff0c;想找些替代的型号。原先使用国产CH340芯片比较多&#xff0c;顺带去官网找下有没有其他小封装的芯片型号。导航比较方便&#xff0c;从官网的产品中心&#xff0c;选择“USB”分类…

Java Code之多态

Java代码 package com.iteye.badpie.javacode.duotai; /** * 人民警察 */public interface IPolice { /** * 抓小偷 */public void catchThief(); }package com.iteye.badpie.javacode.duotai;/*** 人民警察*/ public interface IPolice {/*** 抓小偷*/public void catch…

最小生成树之prim

prim是设置一个初始结点&#xff0c;寻找其周围最小的边权值&#xff0c;并将该结点作为初始结点&#xff0c;继续寻找现在结点周围的边权值的最小值&#xff0c;但要注意如果这次寻找的某个边权值没有上次的小的话仍然保留上一次的边权值&#xff0c;即lowcast的值将会不变。 …

element-ui自定义表头;el-table自定义表头;render-header自定义表头

自定义表头有两种方式&#xff1a;一种是使用render-header 一种是通过设置 Scoped slot 来自定义表头 一、render-header方式 场景&#xff1a;给表头设置自定义按钮&#xff0c;点击时候 批量下载或做其他事件 给当前的那列设置 :render-header <el-table-column align&…

八皇后算法

From: http://blog.csdn.net/feixiaoxing/article/details/6877965 【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 八皇后是一道很具典型性的题目。它的基本要求是这样的&#xff1a;在一个8*8的…

手机进水急救——爆米花可用来吸干水分

第一步&#xff1a;切断电源一旦手机掉到水里或进了水&#xff0c;请马上断开电源。因为手机的损害往往不是由水本身导致的&#xff0c;而是因为水引发的内部元件的短路&#xff0c;甚至进一步导致元件烧坏或电池爆炸。一旦不能立马关掉手机电源的话&#xff0c;如果你的手机电…

NOIP2011 选择客栈

题目描述 Description丽江河边有 n 家很有特色的客栈&#xff0c;客栈按照其位置顺序从1 到n 编号。每家客栈都按照某一种色调进行装饰&#xff08;总共k 种&#xff0c;用整数0 ~ k-1 表示&#xff09;&#xff0c;且每家客栈都设有一家咖啡店&#xff0c;每家咖啡店均有各自的…

vue项目转rem;H5配置rem;px转rem

H5可以配合vant组件库书写项目&#xff0c;和使用rem后vant组件样式变小了解决办法。&#xff08;引入方式&#xff09; 以下是配置rem步骤&#xff1a; 1.安装 flexible和 postcss-px2rem&#xff08;命令行安装&#xff09; lib-flexible 会自动在为你添加 meta name“viewp…