您的位置:首页 > 移动开发 > Android开发

Android BLE 蓝牙实践

2016-06-04 23:58 309 查看

前言

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

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() {
@Override
public void run() {
mBluetoothAdapter.stopLeScan(mScanCallback);
}
},10000);
mBluetoothAdapter.startLeScan(mScanCallback);
}
}

/**
* 扫描回调接口
*/
private LeScanCallback mScanCallback = new LeScanCallback() {
@Override
public 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() {

@Override
public 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","设备已断开");
}
}

@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.d("ble","读取特征值回调");
}

@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.d("ble","特征值改变回调");
}

@Override
public 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() {
@Override
public void run() {
mBluetoothAdapter.stopLeScan(mScanCallback);
}
}, 10000);
mBluetoothAdapter.startLeScan(mScanCallback);
}
}

/**
* 扫描回调接口
*/
private LeScanCallback mScanCallback = new LeScanCallback() {
@Override
public 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() {

@Override
public 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", "设备已断开");
}
}

@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.d("ble", "读取特征值回调");
}

@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.d("ble", "特征值改变回调");
}

@Override
public 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