Android(安卓)低功耗(BLE)蓝牙(三)
本文章经典蓝牙开发目录:
1、权限申请
2、开启蓝牙
3、扫描蓝牙
4、连接蓝牙
5、通信(实现双向通信)(这个是公司的设备,我把步骤说清楚,没法演示~)
6、关闭各种通信
1、2两个步骤和经典蓝牙的步骤是一样的,在这里不在写了,可以看一下上一篇的经典蓝牙的1/2两部哦~~~
Android 蓝牙开发 经典蓝牙和低功耗蓝牙(一)
Android 经典蓝牙开发(二)
开发之前先了解几个概念,大概知道啥意思就行:
BluetoothGatt
这个类提供了 Bluetooth GATT 的基本功能。例如重新连接蓝牙设备,发现蓝牙设备的 Service 等等。
BluetoothGattService
这一个类通过 BluetoothGatt#getService 获得,如果当前服务不可见那么将返回一个 null。这一个类对应上面说过的 Service。我们可以通过这个类的 getCharacteristic(UUID uuid) 进一步获取 Characteristic 实现蓝牙数据的双向传输。
BluetoothGattCharacteristic
这个类对应上面提到的 Characteristic。通过这个类定义需要往外围设备写入的数据和读取外围设备发送过来的数据。
第三步,扫描蓝牙
扫描蓝牙
低功耗蓝牙扫描有两种方式
api>=21 bluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback)
api<21 bluetoothAdapter.startLeScan(leScanCallback)
/** * 扫描 */ public void scanLeDevice(final BluetoothAdapter.LeScanCallback leScanCallback, final ScanCallback scanCallback) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (bluetoothAdapter.isEnabled() && bluetoothLeScanner != null) { bluetoothLeScanner.startScan(scanCallback);//开始搜索 } else { Log.e("mcy", "蓝牙不可用..."); } } else { if (bluetoothAdapter.isEnabled()) { bluetoothAdapter.startLeScan(leScanCallback); //开始搜索 } else { Log.e("mcy", "蓝牙不可用..."); } } Log.e("mcy", "开始扫描..."); }
停止扫描
/** * 停止扫描 */ public void stopScan(BluetoothAdapter.LeScanCallback mLeScanCallback, ScanCallback scanCallback) { Log.e("mcy", "停止扫描..."); if (bluetoothAdapter != null && mLeScanCallback != null) { bluetoothAdapter.stopLeScan(mLeScanCallback); } if (bluetoothLeScanner != null && scanCallback != null) { bluetoothLeScanner.stopScan(scanCallback); } }
还有两个回调的方法,这是用来反馈扫描的结果的:
//api<21回调这个借口 leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { if (!TextUtils.isEmpty(device.getName())) { if (!devicesList.contains(device)) { devicesList.add(device); Log.e("mcy", "扫描到设备-->" + device.getName()); textView.setText(textView.getText() + "\n" + device.getName()); } if (device.getName().equals("00doos009000012147")) {//连接制定的设备。!!!!!测试使用!!!!!! Log.e("mcy", "扫描到设备-->" + device.getName()); bleBTBind.stopScan(leScanCallback, scanCallback); bleBTBind.connectLeDevice(MainActivity.this, device); } } } }; //api>=21回调这个借口 scanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { if (!TextUtils.isEmpty(result.getDevice().getName())) { if (!devicesList.contains(result.getDevice())) { devicesList.add(result.getDevice()); textView.setText(textView.getText() + "\n" + result.getDevice().getName()); } if (result.getDevice().getName().equals("00doos009000012147")) {//连接制定的设备。!!!!!测试使用!!!!!! Log.e("mcy", "扫描到设备-->" + result.getDevice().getName()); bleBTBind.stopScan(leScanCallback, scanCallback); bleBTBind.connectLeDevice(MainActivity.this, result.getDevice()); } } } };
bug11.jpeg 第四步,连接蓝牙
低功耗蓝牙没有配对这么一说,直接连接,连接方式有两种,一种是根据蓝牙的地址获取远程设备连接,连接另一种是蓝牙设备直接连接,传入一个回调接口,反馈连接状态,发现服务状态,可以进行下一步的操作
/** * 连接方式二 */ public void connectLeDevice(Context context, BluetoothDevice device) { bluetoothGatt = device.connectGatt(context, false, mBluetoothGattCallback); } /** * 连接方式一 */ public void connection(Context context, String address) { if (BluetoothAdapter.checkBluetoothAddress(address)) { BluetoothDevice remoteDevice = bluetoothAdapter.getRemoteDevice(address); if (remoteDevice == null) { Log.e("mcy", "设备不可用"); } connectLeDevice(context, remoteDevice); } else { Log.e("mcy", "设备不可用"); } }
连接的时候,需要传递一个接口回调,这个是设备反馈回来的状态,具体的使用,代码注释说的很清楚了,不再赘述。
mBluetoothGattCallback = new BluetoothGattCallback() { //当连接状态发生改变 @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (newState == BluetoothProfile.STATE_CONNECTED) { Log.e("mcy", "连接成功..." + gatt.getDevice().getName()); gatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { Log.e("mcy", "连接断开..."); } else if (newState == BluetoothProfile.STATE_DISCONNECTING) { Log.e("mcy", "连接ing..."); } } //发现新服务,即调用了mBluetoothGatt.discoverServices()后,返回的数据 @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); if (status == BluetoothGatt.GATT_SUCCESS) { Log.e("mcy", "发现服务成功..."); gattService = gatt.getService(UUID.fromString("49535343-fe7d-4ae5-8fa9-9fafd205e455")); indexTpye = 1; if (gattService == null) { indexTpye = 2; gattService = gatt.getService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb")); } if (gattService == null) { Log.e("mcy", "获取bluetoothGattService失败..."); } else { if (indexTpye == 1) { gattCharacteristic = gattService.getCharacteristic(UUID.fromString("49535343-8841-43F4-A8D4-ECBE34729BB3")); } else { gattCharacteristic = gattService.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb")); } if (gattCharacteristic == null) { Log.e("mcy", "获取Characteristic失败..."); } else { bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);//这一句是为了接受蓝牙数据,必须写!!!否则接受不到数据 bleResultCallBack.onDiscoverServicesSuccess(); } } } else { Log.e("mcy", "发现服务失败..."); } } //读取从设备传递过来的数据值,在这里读数据 @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); Log.e("mcy", "onCharacteristicRead..."); } //发送数据后的回调 @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); Log.e("mcy", "onCharacteristicWrite...发送成功后走这个方法"); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); Log.e("mcy", "---蓝牙回传数据onCharacteristicChanged---"); byte[] value = characteristic.getValue(); if (value != null && value.length > 0) { Log.e("mcy", "接收数据" + Arrays.toString(value)); bleResultCallBack.onReturnResult(value); } } };
bug3.jpeg 第五步,通信
通信包括发送数据和读取数据,这里,我们只需要发送数据就行啦,读数据的话,在上面的接口中回调回来了,进行数据处理就行啦
我这里展示了两种发送数据的方式:均使用 handler 处理了一下,没有直接发送。因为Characteristic最长只能发送 20 个字节,如果要是超过 20 个字节,就得循环发送,当然了,接受数据的时候,也是这个样子,一次接收不完,循环接收。
我这里也有一个问题,就是直接发送数据,第二次会失败,一直没找到原因,但是我换handler 方式,就发送成功,若有大佬知道原因,求你联系我,给小女解惑。
/** * 向蓝牙发送数据方式一 */ public void sendDataToBT() { bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);//不写这一句,蓝牙消息会回传不回来 if (gattCharacteristic != null && bluetoothGatt != null) { //设置读数据的UUID for (byte[] datum : data) { Log.e("mcy_devidedPacket", "" + Arrays.toString(datum)); gattCharacteristic.setValue(datum); Message message = new Message(); message.obj = datum; handler1.sendMessage(message); } } } private void writeData() { try { boolean b = bluetoothGatt.writeCharacteristic(gattCharacteristic); if (b) { Thread.sleep(200); } else { for (int i = 0; i < 10; i++) { if (bluetoothGatt.writeCharacteristic(gattCharacteristic)) { return; } } Log.e("mcy", "10次递归发送数据失败" + b); cancleConnection(); } Log.e("mcy", "发送数据是否成功:" + b); } catch (Exception e) { e.printStackTrace(); } } //存储待发送的数据队列 public Queue dataInfoQueue = new LinkedList<>(); private Handler handler2 = new Handler(); private Runnable runnable = new Runnable() { @Override public void run() { send(); } }; /** * 向蓝牙发送数据方式二 */ public void sendDataToBT2() { if (dataInfoQueue != null) { dataInfoQueue.clear(); dataInfoQueue = Utils.splitPacketFor20Byte(data2); handler2.post(runnable); } } private void send() { if (dataInfoQueue != null && !dataInfoQueue.isEmpty()) { //检测到发送数据,直接发送 if (dataInfoQueue.peek() != null) { gattCharacteristic.setValue(dataInfoQueue.poll());//移除并返回队列头部的元素 boolean b = bluetoothGatt.writeCharacteristic(gattCharacteristic); Log.e("mcy", "发送数据是否成功:" + b); } //检测还有数据,延时后继续发送,一般延时100毫秒左右 if (dataInfoQueue.peek() != null) { handler2.postDelayed(runnable, 100); } } }
bug.jpeg 第六步,关闭各种通信
/** * 断开连接 */ public void cancleConnection() { if (bluetoothGatt != null) { bluetoothGatt.close(); Log.e("mcy", "主动断开连接..."); } }
bug6.jpeg ---------------------好啦,放大招-------------------
BleBlueToothService.java
public class BleBlueToothService extends Service { private BluetoothAdapter bluetoothAdapter; private BluetoothGatt bluetoothGatt; private BluetoothLeScanner bluetoothLeScanner; private BluetoothGattCallback mBluetoothGattCallback; private BleResultCallBack bleResultCallBack; private BluetoothGattService gattService; private BluetoothGattCharacteristic gattCharacteristic; private byte[][] data = new byte[][]{{2, 0, 19, 67, 79, 49, 50, 51, 52, 53, 54, 55, 56, 1, 73, -33, 77, -19, -61, -1}, {41, -45, -26, 3}}; private byte[] data2 = new byte[]{2, 0, 19, 67, 79, 49, 50, 51, 52, 53, 54, 55, 56, 1, 73, -33, 77, -19, -61, -1, 41, -45, -26, 3}; private int indexTpye = 0; @Override public void onCreate() { super.onCreate(); //获取蓝牙适配器 bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); mBluetoothGattCallback = new BluetoothGattCallback() { //当连接状态发生改变 @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (newState == BluetoothProfile.STATE_CONNECTED) { Log.e("mcy", "连接成功..." + gatt.getDevice().getName()); gatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { Log.e("mcy", "连接断开..."); } else if (newState == BluetoothProfile.STATE_DISCONNECTING) { Log.e("mcy", "连接ing..."); } } //发现新服务,即调用了mBluetoothGatt.discoverServices()后,返回的数据 @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); if (status == BluetoothGatt.GATT_SUCCESS) { Log.e("mcy", "发现服务成功..."); gattService = gatt.getService(UUID.fromString("49535343-fe7d-4ae5-8fa9-9fafd205e455")); indexTpye = 1; if (gattService == null) { indexTpye = 2; gattService = gatt.getService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb")); } if (gattService == null) { Log.e("mcy", "获取bluetoothGattService失败..."); } else { if (indexTpye == 1) { gattCharacteristic = gattService.getCharacteristic(UUID.fromString("49535343-8841-43F4-A8D4-ECBE34729BB3")); } else { gattCharacteristic = gattService.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb")); } if (gattCharacteristic == null) { Log.e("mcy", "获取Characteristic失败..."); } else { bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);//这一句是为了接受蓝牙数据,必须写!!!否则接受不到数据 bleResultCallBack.onDiscoverServicesSuccess(); } } } else { Log.e("mcy", "发现服务失败..."); } } //读取从设备传递过来的数据值,在这里读数据 @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); Log.e("mcy", "onCharacteristicRead..."); } //发送数据后的回调 @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); Log.e("mcy", "onCharacteristicWrite...发送成功后走这个方法"); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); Log.e("mcy", "---蓝牙回传数据onCharacteristicChanged---"); byte[] value = characteristic.getValue(); if (value != null && value.length > 0) { Log.e("mcy", "接收数据" + Arrays.toString(value)); bleResultCallBack.onReturnResult(value); } } }; } public class BleBlueToothBind extends Binder { public BluetoothAdapter getAdapter() { return bluetoothAdapter; } public void setBleResultCallBack(BleResultCallBack bleResultCallBack) { BleBlueToothService.this.bleResultCallBack = bleResultCallBack; } /** * 扫描 */ public void scanLeDevice(final BluetoothAdapter.LeScanCallback leScanCallback, final ScanCallback scanCallback) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (bluetoothAdapter.isEnabled() && bluetoothLeScanner != null) { bluetoothLeScanner.startScan(scanCallback);//开始搜索 } else { Log.e("mcy", "蓝牙不可用..."); } } else { if (bluetoothAdapter.isEnabled()) { bluetoothAdapter.startLeScan(leScanCallback); //开始搜索 } else { Log.e("mcy", "蓝牙不可用..."); } } Log.e("mcy", "开始扫描..."); } /** * 停止扫描 */ public void stopScan(BluetoothAdapter.LeScanCallback mLeScanCallback, ScanCallback scanCallback) { Log.e("mcy", "停止扫描..."); if (bluetoothAdapter != null && mLeScanCallback != null) { bluetoothAdapter.stopLeScan(mLeScanCallback); } if (bluetoothLeScanner != null && scanCallback != null) { bluetoothLeScanner.stopScan(scanCallback); } } /** * 连接方式二 */ public void connectLeDevice(Context context, BluetoothDevice device) { bluetoothGatt = device.connectGatt(context, false, mBluetoothGattCallback); } /** * 连接方式一 */ public void connection(Context context, String address) { if (BluetoothAdapter.checkBluetoothAddress(address)) { BluetoothDevice remoteDevice = bluetoothAdapter.getRemoteDevice(address); if (remoteDevice == null) { Log.e("mcy", "设备不可用"); } connectLeDevice(context, remoteDevice); } else { Log.e("mcy", "设备不可用"); } } private Handler handler1 = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); writeData(); } }; /** * 向蓝牙发送数据方式一 */ public void sendDataToBT() { bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);//不写这一句,蓝牙消息会回传不回来 if (gattCharacteristic != null && bluetoothGatt != null) { //设置读数据的UUID for (byte[] datum : data) { Log.e("mcy_devidedPacket", "" + Arrays.toString(datum)); gattCharacteristic.setValue(datum); Message message = new Message(); message.obj = datum; handler1.sendMessage(message); } } } private void writeData() { try { boolean b = bluetoothGatt.writeCharacteristic(gattCharacteristic); if (b) { Thread.sleep(200); } else { for (int i = 0; i < 10; i++) { if (bluetoothGatt.writeCharacteristic(gattCharacteristic)) { return; } } Log.e("mcy", "10次递归发送数据失败" + b); cancleConnection(); } Log.e("mcy", "发送数据是否成功:" + b); } catch (Exception e) { e.printStackTrace(); } } //存储待发送的数据队列 public Queue dataInfoQueue = new LinkedList<>(); private Handler handler2 = new Handler(); private Runnable runnable = new Runnable() { @Override public void run() { send(); } }; /** * 向蓝牙发送数据方式二 */ public void sendDataToBT2() { if (dataInfoQueue != null) { dataInfoQueue.clear(); dataInfoQueue = Utils.splitPacketFor20Byte(data2); handler2.post(runnable); } } private void send() { if (dataInfoQueue != null && !dataInfoQueue.isEmpty()) { //检测到发送数据,直接发送 if (dataInfoQueue.peek() != null) { gattCharacteristic.setValue(dataInfoQueue.poll());//移除并返回队列头部的元素 boolean b = bluetoothGatt.writeCharacteristic(gattCharacteristic); Log.e("mcy", "发送数据是否成功:" + b); } //检测还有数据,延时后继续发送,一般延时100毫秒左右 if (dataInfoQueue.peek() != null) { handler2.postDelayed(runnable, 100); } } } /** * 断开连接 */ public void cancleConnection() { if (bluetoothGatt != null) { bluetoothGatt.close(); Log.e("mcy", "主动断开连接..."); } } } @Override public IBinder onBind(Intent intent) { return new BleBlueToothBind(); }}
使用:
bleConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bleBTBind = ((BleBlueToothService.BleBlueToothBind) service); if (bleBTBind.getAdapter() != null) { //判断蓝牙是否开启 if (!bleBTBind.getAdapter().isEnabled()) { //打开蓝牙 openBlueSync(MainActivity.this, openBTCode); } else { //========================开始执行工作============================= bleBTBind.scanLeDevice(leScanCallback, scanCallback); final StringBuilder stringBuilder = new StringBuilder(); bleBTBind.setBleResultCallBack(new BleResultCallBack() { //连接成功回调 @Override public void onDiscoverServicesSuccess() { bleBTBind.stopScan(leScanCallback, scanCallback); bleBTBind.sendDataToBT();//方式一// bleBTBind.sendDataToBT2();//方式二 } //蓝牙返回数据回调 @Override public void onReturnResult(byte[] data) { bleBTBind.stopScan(leScanCallback, scanCallback); for (byte byteChar : data) { stringBuilder.append(String.format("%02X ", byteChar)); } String returnedPacket = stringBuilder.toString().replace(" ", ""); byte[] packetByte = Utils.hexStringToByteArray(returnedPacket); if (packetByte.length - 5 == Utils.getLengthFromToken(packetByte)) { Log.e("mcy_returnedPacket", returnedPacket); bleBTBind.cancleConnection();//取消连接 } } }); } } else { Log.e("mcy", "此设备不支持蓝牙"); } } @Override public void onServiceDisconnected(ComponentName name) { bleBTBind = null; } }; bindService(new Intent(this, BleBlueToothService.class), bleConnection, BIND_AUTO_CREATE);
github,地址 : https://github.com/Mchunyan/BlueToothTest
-----------------------The End-----------------
更多相关文章
- 宏锦软件 Android(安卓)的 ListView 使用详解
- 浅析android的mvp模式
- android调用系统相机实现拍照功能
- Android(安卓)JNI和NDK学习(08)--JNI实例一 传递基本类型数据
- ContentResolver
- 微信聊天记录删除怎么恢复?iOS安卓数据迁移
- android学习十二(android的Content Provider(内容提供器)的使用)
- Android(安卓)Architecture Component源码解析之LiveData
- Android应用架构