声明
本文主要讲解BLE(低功耗蓝牙4.0以上)的使用和封装,为了UI层方便拿取数据展示,统一对蓝牙搜索、连接、数据交互、蓝牙协议等封装为lib。
一.BLE简介
为什么要学习蓝牙技术,蓝牙作为一种成熟、低功耗无线通信技术的先锋,在可穿戴设备领域中扮演着越来越重要的作用。
BLE分为三部分:Service,Characteristic,Descriptor。这三部分都是使用UUID来作为唯一标识符加以区分。一个BLE终端可以包含多个Service,一个Service可以包含多个Characteristic,而一个Characteristic包含一个value和多个Descriptor,一个Descriptor只包含一个value。UUID格式为:0000ffe1-0000-1000-8000-00805f9b34fb
二.蓝牙前期简单介绍
1.添加权限
<uses-permission android:name="android.permission.BLUETOOTH" /><!--蓝牙管理-->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><!--蓝牙操作权限-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><!--在有些机型上需要获取位置信息才能扫描到蓝牙设备,此权限在api23+需要动态申请-->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" /> <!--表示此app只支持拥有BLE的设备上运行-->
如果你不确定你的app使用的设备是否支持低功耗蓝牙,但又想让支持的设备使用低功耗蓝牙,把标志位改为false,同时去代码中判断设备是否支持BLE:
// 是否支持蓝牙低功耗广播(4.3+)
if (!getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
finish()//此处可以根据特定需求去改变处理
return false;
}
2.蓝牙适配器BluetoothAdapter
有两种获取方式:
1).BluetoothManager bluetoothManager =(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
2).BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
此处需要做非空判断,为null则表示设备不支持蓝牙。
BluetoothAdapter方法描述:
- isEnabled() 判断系统蓝牙是否打开
- disable() 无弹窗提示关闭系统蓝牙
- enable() 无弹窗提示打开系统蓝牙(此操作有点不友好!)
- startDiscovery() 开始搜索设备 —–适合经典蓝牙和低功耗蓝牙两种
- cancelDiscovery() 取消搜索设备
- startLeScan() 开始搜索设备 —–适合扫描低功耗蓝牙,但是在api21以上被标记废弃使用
- stopLeScan() 停止搜索设备
- startScan() 开始搜索设备 —–api21以上扫描低功耗蓝牙,通过bluetoothAdapter.getBluetoothLeScanner()方法获取
- stopScan() 停止搜索设备
- stopScan() 停止搜索设备
说明:startDiscovery()方法在大多数手机上是可以同时发现经典蓝牙和低功耗蓝牙(BLE)的,但是startDiscovery()的回调无法返回BLE的广播,所以无法通过广播识别设备,而且startDiscovery()扫描BLE效率比startLeScan()低很多。因此需要根据具体的需求去做适配,才能更高效的搜寻蓝牙。PS: startLeScan()和startScan()有重载方法可以指定规则,参数去搜索。
3.蓝牙协议BluetoothGatt
获取方式:
BluetoothGatt mBluetoothGatt = device.connectGatt(getContext(), false, mGattCallback);//创建连接
BluetoothGatt方法描述:
- connect() 连接远程设备
- discoverServices() 搜索连接设备所支持的service
- disconnect() 断开与远程设备的GATT连接
- close() 关闭GATT Client端,释放资源
- readCharacteristic(characteristic) 在指定的characteristic特征端口中读取数据
- writeDescriptor(characteristic) 在指定的characteristic特征端口中写入数据
- setCharacteristicNotification(characteristic, enabled) 设置当指定characteristic特征端口值变化时,是否发出通知返回
- readRemoteRssi() 读取当前连接设备信号值,返回数值为负,值越小信号越弱。
- getServices() 获取远程设备所支持的services
- getDevice() 获取已连接的设备信息
三.蓝牙正式封装
与BLE蓝牙交互分为三步骤:搜索、连接、读写数据。BLE蓝牙无需配对即可连接
1.搜索、连接蓝牙
在搜索、连接蓝牙之前,首先对官方提供的BluetoothLeService进行一层封装,此类无非就是对BluetoothGatt协议的的获取和使用(连接设备,断开连接,发现服务,读写设备等操作)。本文采用单列模式和接口回调方式进行解耦分离、数据交互,源码实现方式为服务+广播的形式。PS:ble蓝牙官方demo网上资源还是比较多的,如果没有可以在下方留言。
package com.csym.bluetoothlib;
import android.bluetooth.BluetoothAdapter;
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 com.orhanobut.logger.Logger;
import java.util.List;
/**
* 蓝牙服务管理类
* Created by ${zhoupeng} on 2016/8/10.
*/
public class BluetoothLeService {
private BluetoothManager mBluetoothManager;//用来获取蓝牙适配器
private BluetoothAdapter mBluetoothAdapter;//蓝牙适配器,处理系统蓝牙是否打开,搜索设备
private BluetoothGatt mBluetoothGatt;//发现蓝牙服务,根据特征值处理数据交互
private BluetoothDevice mBluetoothDevice = null;//蓝牙设备
private static BluetoothLeService INSTANCE = null;//单列模式
private static Context mContext;//上下文
/**
* 是否连接
*/
private boolean isConnected = false;
public boolean isConnected() {
return isConnected;
}
public void setConnected(boolean connected) {
isConnected = connected;
}
/**
* 获取上下文
*
* @return context
*/
private Context getContext() {
return mContext;
}
private BluetoothLeService() {
boolean value = initialize();
if (!value) Logger.e("蓝牙适配器adapter初始化失败!!!");
}
/**
* 单列模式,确保唯一性
*
* @param context 上下文
* @return 对象
*/
public static BluetoothLeService getInstance(Context context) {
mContext = context;
if (INSTANCE == null) {
synchronized (BluetoothLeService.class) {
if (INSTANCE == null) {
INSTANCE = new BluetoothLeService();
}
}
}
return INSTANCE;
}
/**
* 初始化bluetooth adapter
*
* @return 为null返回false
*/
private boolean initialize() {
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getContext().getSystemService(Context.BLUETOOTH_SERVICE);
}
if (mBluetoothManager == null) return false;
mBluetoothAdapter = mBluetoothManager.getAdapter();
return mBluetoothAdapter != null;
}
/**
* 启用或者禁用通知\标志返回特性 true, if the requested notification status was set successfully
*
* @param characteristic 蓝牙特征对象
* @param enabled 是否允许
* @return 设置成功或失败
*/
public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
return !(mBluetoothAdapter == null || mBluetoothGatt == null)
&& mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
}
public boolean writeDescriptor(BluetoothGattDescriptor bluetoothGattDescriptor) {
return !(bluetoothGattDescriptor == null || mBluetoothGatt == null)
&& mBluetoothGatt.writeDescriptor(bluetoothGattDescriptor);
}
/**
* 发现数据通道服务
*
* @return true or false
*/
public boolean discoverServices() {
return !(!isConnected() || mBluetoothGatt == null)
&& mBluetoothGatt.discoverServices();
}
/**
* 读取蓝牙数据
*
* @param characteristic 蓝牙特征值
* @return true or false
*/
public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
return !(mBluetoothAdapter == null || mBluetoothGatt == null)
&& mBluetoothGatt.readCharacteristic(characteristic);
}
/**
* 往设备中写入数据
*
* @param characteristic 蓝牙特征值
* @return true or false
*/
public boolean writeCharecteristic(BluetoothGattCharacteristic characteristic) {
return !(mBluetoothAdapter == null || mBluetoothGatt == null)
&& mBluetoothGatt.writeCharacteristic(characteristic);
}
/**
* 获取gatt服务列表
*
* @return 远程设备提供的gatt服务列表(功能通道)
*/
public List<BluetoothGattService> getSupportedGattServices() {
if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getServices();
}
/