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; }
}
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories