前段时间做了一个有关于安卓蓝牙BLE设备的开发项目,主要的功能包括了搜索蓝牙ble设备和ble设备的数据读写等等,本篇博客用于记录安卓蓝牙ble设备的通信的细节。
 其实关于BLE设备的通信在API中已经讲地比较清楚了,这里只是做一个总结,如果要进行BLE设备的开发,首先可以阅读API.
BLE有关API


BLE设备的定义和特点

 BLE-维基百科
 对于BLE设备的定义,我们只需要看维基百科的说明就好了,简单的说明一下BLE的原理。
 首先对于BLE硬件来说,它能够决定什么时候去发出请求和外接的设备进行连接,安卓官方称之为advertisement,市面上见到的BLE设备,比如蓝牙音响,一打开就能够被手机检测到,原因是其硬件上设定了会一直发出advertisement,直到连接上设备为止。但是实际上BLE设备本身是可以控制自己要不要发出advertisement的,如果BLE设备没有发出这个advertisement,那么我们是搜索不到它的。
 对于BLE设备还有一个坑,那就是BLE设备只能连接一个设备,也就是说,一旦当BLE设备连接了一台手机后,其他的手机上就无法搜索到这台BLE设备了。我猜测应该是BLE设备连接之后就不会发出advertisement了,导致其他设备搜索不到。


安卓上进行BLE开发的步骤

1、AndroidManifest中声明权限

对于BLE开发我们需要声明两个权限

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

 其中第一个权限是只要我们进行与蓝牙相关的操作都需要声明的,比如你的APP只是希望能够被别人检测到,然后通过蓝牙交换数据,就需要声明这一个。
 第二个权限是如果你的设备需要主动搜索蓝牙设备,或者是对蓝牙的某些设置进行更改就需要这个权限,一般来说我们在进行BLE开发的时候会同时声明这两个权限。
 因为不是所有的手机上都有蓝牙BLE功能,需要API18(Android4.3)以上才能进行BLE的开发,所以我们可以声明features过滤掉不满足要求的设备

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

2、获取BluetoothAdapter

 BluetoothAdapter是用来获取可以连接的BLE设备列表的一个类,想要和BLE设备通信当然需要先能够搜索到可用的BLE设备。安卓获取BluetoothAdapter的实例的方式很简单。首先通过调用系统服务获得BluetoothManager的实例,然后调用BluetoothManager的getAdapter()方法就可以了,代码如下:

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

 当然,这里的mBluetoothAdapter我们最好保存为类的数据成员,因为之后我们还会用到它去获取当前可连接的BLE设备的列表。

3、开启蓝牙

 得到了BluetoothAdapter的实例之后我们就可以用其中的方法去搜索设备了,但是在此之前,我们首先要打开手机上的蓝牙功能,这个可以通过用户自己找到蓝牙去打开,但是这样很明显会影响用户体验。没有人想在使用APP的时候还需要去找到系统的蓝牙并打开。安卓API为我们提供了在APP中开启系统蓝牙的方法:即调用BluetoothAdapter中的enable()方法。
 为了严谨,我们需要判断上一步获得的mBluetoothAdapter是否为空(只有当设备不支持BLE的时候这个实例才会为null)

public void enable() {if(mBluetoothAdapter == null || ! mBluetoothAdapter.isEnabled()) {mBluetoothAdapter.enable();}
}

这里我们调用了mBluetoothAdapter.isEnabled(),避免重复打开蓝牙。

4、搜索BLE设备

 打开蓝牙之后,我们终于可以搜索BLE设备了。这个部分需要注意的问题主要是搜索时间的问题,因为搜索BLE设备是一个很费电的过程,所以搜索的时间不应该设置太长。在开始搜索的同时,我们应该设置一个Handler,然后调用Handler的postDelayed(Runnable r)方法去设定在搜索一定时间后停止搜索。
 将搜索BLE设备的过程独立写成一个方法,这样如果需要多次搜索的话只需要再次调用此方法即可。另外应该设立一个标志位mScanning,在调用搜索方法的时候应该判断标志位,如果现在正在搜索就不应该再次进行搜索。

public void scanLeDevice(boolean enable) {if(enable) {mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback);mBLEListActivity.setTextInfo();}}, SCAN_PERIOD);mScanning = true;mBluetoothAdapter.startLeScan(mLeScanCallback);} else {mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback);}
}

 这段方法的核心主要是两个方法:mBluetoothAdapter.startLeScan(mLeScanCallback)和
mBluetoothAdapter.stopLeScan(mLeScanCallback)。分别对应开始BLE设备的搜索和停止BLE设备的搜索。需要注意的是这两个方法只针对于BLE设备,对于普通的蓝牙设备,调用这个方法是搜索不到的。
 但是你可能要问了,我们怎么才能得到搜索的结果呢?请注意,在这两个方法中都有一个共同的参数mLeScanCallback,这个mLeScanCallback就是我们获取搜索结果的载体。
 让我们一起来看看mLeScanCallback的定义

private BluetoothAdapter.LeScanCallback mLeScanCallback =new BluetoothAdapter.LeScanCallback() {@Overridepublic void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {if(!mBLEListActivity.mBLEArrayList.contains(device)) {mBLEListActivity.addBluetoothItem(device);mBLEListActivity.runOnUiThread(new Runnable() {@Overridepublic void run() { mBLEListActivity.mBLEAdapter.notifyDataSetChanged();mBLEListActivity.mListView.setAdapter(mBLEListActivity.mBLEAdapter);}   });}}};

 可以看到mLeScanCallback实际上是一个BluetoothAdapter.LeScanCallback的实例。LeScanCallback中包含了一个回调方法onLeScan(),我们通过重写这个方法去进行一系列操作。
 到这里我们基本上就可以理解了,实际上BLE的搜索的实现是通过回调方法完成的,当检测到BLE设备的时候,就调用一次onLeScan()方法,然后我们应该在里面去保存搜索到的device,比如放在一个List中。一般的处理方法是将搜索到的device放在ListView中进行显示,然后响应用户的点击进行连接。这不属于BLE开发的范畴,所以暂不赘述。

5、与指定BLE设备进行通信

 通过第4步我们能够搜索到我们想要的device,现在应该开始通信了。通信的第一步就是和BLE设备建立连接。安卓用BluetoothGatt类来管理这种连接,首先我们需要获得一个BluetoothGatt实例,只需要调用BluetoothDevice的connectGatt()方法即可。

if(mBLEDevice != null) {Log.d(TAG, "建立连接");mBluetoothGatt = mBLEDevice.connectGatt(mBLEService, true, mGattCallback);
} else {Log.e(TAG, "没找到特定的BLE设备,连接无法建立");
}

这里有三个参数,重点说一下第三个参数mGattCallback,这时连接过程中最为重要的一个参数,其定义如下(此部分为安卓官方的代码)

// A service that interacts with the BLE device via the Android BLE API.
public class BluetoothLeService extends Service {private final static String TAG = BluetoothLeService.class.getSimpleName();private BluetoothManager mBluetoothManager;private BluetoothAdapter mBluetoothAdapter;private String mBluetoothDeviceAddress;private BluetoothGatt mBluetoothGatt;private int mConnectionState = STATE_DISCONNECTED;private static final int STATE_DISCONNECTED = 0;private static final int STATE_CONNECTING = 1;private static final int STATE_CONNECTED = 2;public final static String ACTION_GATT_CONNECTED ="com.example.bluetooth.le.ACTION_GATT_CONNECTED";public final static String ACTION_GATT_DISCONNECTED ="com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";public final static String ACTION_GATT_SERVICES_DISCOVERED ="com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";public final static String ACTION_DATA_AVAILABLE ="com.example.bluetooth.le.ACTION_DATA_AVAILABLE";public final static String EXTRA_DATA ="com.example.bluetooth.le.EXTRA_DATA";public final static UUID UUID_HEART_RATE_MEASUREMENT =UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);// Various callback methods defined by the BLE API.private final BluetoothGattCallback mGattCallback =new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) {String intentAction;if (newState == BluetoothProfile.STATE_CONNECTED) {intentAction = ACTION_GATT_CONNECTED;mConnectionState = STATE_CONNECTED;broadcastUpdate(intentAction);Log.i(TAG, "Connected to GATT server.");Log.i(TAG, "Attempting to start service discovery:" +mBluetoothGatt.discoverServices());} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {intentAction = ACTION_GATT_DISCONNECTED;mConnectionState = STATE_DISCONNECTED;Log.i(TAG, "Disconnected from GATT server.");broadcastUpdate(intentAction);}}@Override// New services discoveredpublic void onServicesDiscovered(BluetoothGatt gatt, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);} else {Log.w(TAG, "onServicesDiscovered received: " + status);}}@Override// Result of a characteristic read operationpublic void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {if (status == BluetoothGatt.GATT_SUCCESS) {broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);}}...};
...
}

 代码比较繁杂,不需要仔细看。我们只需要知道mGattCallback是一个BluetoothGattCallback的实例,里面有很多的回调方法就够了。下面我会说到一些比较重要的回调方法,这些回调方法主要是用于检测通信过程中状态的改变的。
 连接的过程中我们传入了mGattCallback,然后mGattCallback中的回调方法是需要我们自己去重写的,在对应不同状态的时候去进行不同的动作。比较重要的方法有以下几个:

onConnectionStateChange()

public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);if(newState == BluetoothProfile.STATE_CONNECTED) {mBluetoothGatt = gatt;if(mBluetoothGatt != null) {mBluetoothGatt.discoverServices();} else {Log.d(TAG, "mBluetoothGatt为空");}} else if(newState == BluetoothProfile.STATE_DISCONNECTED) {mActivity.runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(mActivity, "连接断开,请检查设备", Toast.LENGTH_LONG).show();}   });}
}

 这个方法主要检测的是设备的连接状态的改变,每当设备连接状态发生改变的时候,即从连接到断开或者从断开到连接的时候都会调用此方法。我们应该判断newState的值,然后在连接成功的时候去调用mBluetoothGatt.discoverServices()(之后我会说明这个方法的作用),在连接断开的时候进行清理,并且告知用户连接断开了。

onServiceDiscovered()

public void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);if(status == BluetoothGatt.GATT_SUCCESS) {mActivity.runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(mActivity, "已连接", Toast.LENGTH_LONG).show();}   });try {getBtgService();getBtgCharacteristic();mBLEAudioTrack.play();if(mReadCharacteristic != null) {mBluetoothGatt.setCharacteristicNotification(mReadCharacteristic, true);}}catch(Exception e) {e.printStackTrace();}} else {Log.e("TAG", "连接失败");}
}

 刚才我们提到了在连接成功时候需要调用mBluetoothGatt.discoverServices(),为什么呢?这就涉及到蓝牙BLE通信上的一个设计,蓝牙的数据传输并不是一位一位传过来的,而是通过Characteristic进行封装后传过来的,一个Characteristic中包装了一组数据。在Characteristic上层,还有Service,封装了多个Charateristic。在实际的通信中,我们需要首先获取BluetoothGattService,然后通过BluetoothGattService的实例去获取BluetoothGattCharacteristic。
 而在获取BluetoothGattService之前,我们需要先获取服务,这个获取服务相当于是告诉蓝牙设备我要开始进行数据交换了,请你准备好,mBluetoothGatt.discoverServices()就是完成这个功能的。当找到了服务之后,安卓就会自动调用上面的onServicesDiscovered()方法,然后我们就可以在里面去获取BluetoothGattService和BluetoothGattCharacteristic。
 怎么获取Service和Characteristic呢?
 这里用到了UUID,关于UUID的基本知识请参照维基百科-UUID
在Java中UUID通过java.util.UUID.fromString(String str)来产生的,在这一部分需要和BLE硬件相适应,确定BLE设备上发送数据和接收数据的Service和Characteristic的UUID号。以下是我所使用的BLE设备的Service和Characteristic的获取方式

首先用UUID号得到UUID对象

private static final String SERVICE_NAME = "0000FFF0-0000-1000-8000-00805F9B34FB";
private static final UUID SERVICE_UUID = UUID.fromString(SERVICE_NAME);
private static final String CHARC_NAME = "0000FFF6-0000-1000-8000-00805F9B34FB";
private static final UUID CHARC_UUID = UUID.fromString(CHARC_NAME);

获取Service实例

public void getBtgService() {mBluetoothGattService = mBluetoothGatt.getService(SERVICE_UUID);Log.i(TAG, "get service"+mBluetoothGattService.toString());
}

获取Characteristic实例

public void getBtgCharacteristic() {mReadCharacteristic = mBluetoothGattService.getCharacteristic(CHARC_UUID);mWriteCharacteristic = mBluetoothGattService.getCharacteristic(CHARC_UUID);Log.i(TAG, "get characteristic"+mReadCharacteristic.toString());List<BluetoothGattDescriptor> descriptors = mReadCharacteristic.getDescriptors();for (BluetoothGattDescriptor bgp : descriptors) {Log.i(TAG, "setCharacteristicNotification: " + bgp);bgp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);mBluetoothGatt.writeDescriptor(bgp);}
}

 在获取mReadCharacteristic的时候需要注意,我们通过mReadCharacteristic.getDescriptors()获取了它的描述符,然后对下面的所有描述符都设置了属性,这样做的原因是因为之后我们需要回调方法onCharacteristicChanged(),如果不这样设置,那么当读数据成功后不会进入onCharacteristicChanged()这个回调方法中,这样我们就无法获得之后的数据了。所以为了让读操作能够一直持续,我们需要设置其Descriptor。
 注意,在获取了Service和Characteristic之后实际上我们就可以开始进行数据通信了,我在这里调用了方法

if(mReadCharacteristic != null) {mBluetoothGatt.setCharacteristicNotification(mReadCharacteristic, true);}

通过这个方法,现在就能监听安卓设备是否收到BLE设备发来的数据,如果收到了发来的数据,会调用方法onCharacteristicRead(),下面介绍这个方法。

onCharacteristicRead()

 public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {if (status == BluetoothGatt.GATT_SUCCESS) {readCharacteristicValue(mReadCharacteristic);mBluetoothGatt.setCharacteristicNotification(mReadCharacteristic, true);}}

 onCharacteristicRead()这个方法会在接收数据的时候被调用,在这里面我们可以去得到BLE设备发过来的数据,通过characteristic.getValue()即可。这里对数据的操作被封装在readCharacteristicValue(mReadCharacteristic)中了。
 注意,在这个方法中,一定要再次调用mBluetoothGatt.setCharacteristicNotification(mReadCharacteristic, true);这样才能保证下一次接收到数据时又能进入此回调方法。这样就能保证连续地接收BLE设备的数据了。

onCharacteristicWrite()

public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,int status) {switch(status) {case BluetoothGatt.GATT_SUCCESS:Log.d(TAG, "write data success");break;case BluetoothGatt.GATT_FAILURE:Log.d(TAG, "write data failed");case BluetoothGatt.GATT_WRITE_NOT_PERMITTED:Log.d(TAG, "write not permitted");}super.onCharacteristicWrite(gatt, characteristic, status);
}

如果需要向BLE设备写数据其实是与读数据相似的处理,就不赘述了。

6、结束通信时,释放资源

 这一部分比较简单,只需要调用如下的close()方法即可

public void close() {if (mBluetoothGatt == null) {return;}mBluetoothGatt.close();mBluetoothGatt = null;
}

结语

 这篇博客作为我博客生涯的一个开始,以后我会在学习到新的知识的时候尽量多地去总结,会保持博客的更新。

安卓蓝牙BLE设备开发相关推荐

  1. 二、搜索蓝牙并连接(安卓蓝牙ble教程)

    1.MainActivity.java 注:如果复制代码进项目时显示红色,请按ALT+ENTER键导包(import class) package club.stm32;import android. ...

  2. 安卓连接ble蓝牙设备教程(目录)

    安卓连接ble蓝牙设备教程(目录) 零.新建android工程(安卓蓝牙ble教程) 一.权限和build.gradle配置并开启蓝牙(安卓蓝牙ble教程) 二.搜索蓝牙并连接(安卓蓝牙ble教程) ...

  3. 安卓基于BLE的蓝牙开发入门

    BLE蓝牙开发简单入门 BLE背景介绍 引言 BLE简介 Gatt协议以及必备知识 蓝牙开发涉及的API介绍 BLE实战准备 真机调试 权限准备 写两个简单的页面 扫描设备主界面 扫描设备信息界面 实 ...

  4. 安卓读取蓝牙BLE设备信息

    安卓读取蓝牙BLE设备信息 简介 轮询方式代码实现 监听广播方式代码实现 简介 目前,许多项目都会涉及与BLE设备进行交互的功能,接下来说一下读取BLE设备信息的具体实现流程.安卓BLE相关接口介绍详 ...

  5. 基于intel芯片的安卓蓝牙4.0 BLE通信总结

    基于intel芯片的安卓蓝牙4.0 BLE通信问题总结 使用设备: 台电 x98 air 3G 系统:安卓4.4.4 系统搭建: 1.安装ADT驱动,可以搜索intel_mobile_usb_driv ...

  6. BLE蓝牙HID设备开发(BL602)

    总述 BLE蓝牙在物联网中使用很广泛,许多WiFi物联网芯片都带有BLE蓝牙,天猫精灵找队友也是有使用BLE蓝牙广播实现的.BLE蓝牙缺点是不适合大数据传输的场景. BLE蓝牙 BLE蓝牙主机开发基本 ...

  7. 蓝牙BLE(协议栈、OSAL、蓝牙APP工具)

    目录 蓝牙配对和绑定 蓝牙4.0 BLE 信道(RF Channel) BLE协议栈分层 PHY层(Physical layer 物理层) LL层(Link Layer 链路层) HCI层(Host ...

  8. 安卓蓝牙实现即时通讯功能

    安卓蓝牙实现即时通讯功能 本demo是<Android智能穿戴设备开发指南>书中的一块内容,实现了两台手机基于蓝牙进行即时通讯的功能. demo演示如下: 结构图 主要代码 MainAct ...

  9. 安卓蓝牙开发的几个版本区别

    不得不说,这有时候真的坑爹. 你 搜索安卓蓝牙开发,有的大谈4.0结果还是什么socket那些,这是安卓4.0,不是蓝牙4.0啊 安卓4.2以及以下才是这种 4.3以后就可以ble了,低功耗,更碉堡 ...

最新文章

  1. 超越英伟达的,不会是另一款GPU!中国公司发布首款数据流AI芯片
  2. 输入一个正整数,求它各位数的数字之和
  3. 最大玻尔兹曼分布的mASK信号在AWGN信道下的容量计算
  4. python查找指定文件夹_python实现在目录中查找指定文件的方法
  5. jeecms 代码生成 Tools
  6. css不继承上级样式_这个笔记《CSS基本概念》,让菜鸟轻松学会给网页穿外衣
  7. SPD软件(医用耗材管理系统)应用效果分析
  8. 课程思政与c语言程序设计,C语言程序设计课程思政教学改革教学设计.doc
  9. 2021年起,WPS Office纳入全国计算机等级考试二级考试软件
  10. 80c51流水灯程序汇编语言,stc89c51单片机流水灯程序.doc
  11. MPU6050 加速计滤波
  12. 王道考研操作系统复习笔记
  13. ThinkPad笔记本无法调节亮度
  14. hive 中创建表的三种方式
  15. android声音播放函数双声道合并,Android音频编辑之音频合成功能
  16. 全国青少年编程等级考试python一级真题2022年3月(含题库答题软件账号)
  17. 蓝桥杯——算法基础 逗志芃的暴走PYTHON
  18. 如何实现游戏中的段位排行榜?
  19. 家庭nas方案_openmediavault入门:家庭NAS解决方案
  20. 装的系统没有截图和计算机工具栏,win10百度浏览器工具栏找不到“截图”按钮怎么办...

热门文章

  1. 【MapReduce】基础案例 ---- Reduce Join 实现数据合并(表连接)
  2. 数据库系统工程师——第一章 计算机系统知识
  3. Word一打开,目录、页码变成代码(Word2019)
  4. Learning From Video DRL OpenAI Five
  5. 应用之星:十问十答,让你更快了解H5制作和app开发
  6. 思维导图by lrllrl
  7. 东北大学数据科学基础(MATLAB)-笔记
  8. RxJava操作符(05-结合操作)
  9. 多余额智能扣减账户系统设计
  10. maple 假设_maple基本函数