Android手机蓝牙总结之低功耗蓝牙
低功耗蓝牙也就是蓝牙4.0以后的版本,在android手机是4.3以后才支持。在项目中,开始使用了传统蓝牙去进行连接,发现失败了,始终没办法连接上。后来发现蓝牙硬件是4.0后的蓝牙版本,所以下面记录下android4.3版本后的蓝牙使用。
首先,需要明白一些专业的术语,GATT,characteristics,Properties,Descriptor,Service,UUID。
GATT
GATT是蓝牙连接后读写蓝牙数据的通用规范。它基于ATT通讯协议(Attribute Protocol),目的是在传输信息过程中使用尽量少的数据。在传输时,信息将以特征值(characteristics)和服务(services)的信息传输。这些信息都具有唯一的UUID。下面分别介绍一些基本概念。
characteristics
特征值。可以理解为一个数据类型。包括一个值(Value),一个属性(Property)和0至多个对值的描述(Descriptor)。
Properties
定义了characteristic的Value如何被使用,以及characteristic的Descriptor如何被访问。
Descriptor
是与Characteristic值相关的描述,例如范围、计量单位等。
Service
是Characteristic的集合。一个Service一般包含多个Characteristic。
UUID
唯一标示符,每个Service,Characteristic,Descriptor,都一个唯一的UUID。以后在对它们操作时,可以通过它们的UUID查找到对应的Service,Characteristic或者Descriptor
判断是否支持蓝牙
public boolean checkIfSupportBle() {
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);}
开启蓝牙
这里有两种方式:
public void enableBluetooth(Activity activity) {
if (bleAdapter == null || !bleAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
public void enableBluetooth() {
if (bleAdapter == null || !bleAdapter.isEnabled()) {
bleAdapter.enable();
}
}
扫描广播包
有两种方式获得BluetoothAdapter
第一种通过先获取BluetoothManager,然后获得BlueAdapter
private BluetoothManager bleManager;
private BluetoothAdapter bleAdapter;
bleManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
bleAdapter = bleManager.getAdapter();
第二种是直接获取BluetoothAdapter
BluetoothAdapter bleAdapter = BluetoothAdapter.getDefaultAdapter();
获取蓝牙匹配设备
public List<BluetoothDevice> getBoundDevices() {
List<BluetoothDevice> devices = new ArrayList<>();
Set<BluetoothDevice> boundDevices = bleAdapter.getBondedDevices();
for (BluetoothDevice device : boundDevices) {
//对device进行其他操作,比如连接等。
devices.add(device);
}
return devices;
}
开始扫描
public void startLeScan(BluetoothAdapter.LeScanCallback leScanCallback) {
bleAdapter.startLeScan(leScanCallback);
}
//加入蓝牙过滤
public void startLeScan(UUID[] uuids, BluetoothAdapter.LeScanCallback leScanCallback) {
bleAdapter.startLeScan(uuids, leScanCallback);
}
关闭蓝牙搜索
public void stopLeScan(BluetoothAdapter.LeScanCallback leScanCallback) {
bleAdapter.stopLeScan(leScanCallback);
}
实现BluetoothAdapter.LeScanCallback接口
public class MyLeScanCallback implements BluetoothAdapter.LeScanCallback {
private String mac;
private Context mContext;
private LeScanInterface leScanInterface;
public MyLeScanCallback(Context mContext, String mac) {
this.mac = mac;
this.mContext = mContext;
}
public MyLeScanCallback(Context mContext) {
this.mContext = mContext;
}
@Override
public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
String bleMac = bluetoothDevice.getAddress();
if (TextUtils.equals(mac, bleMac)) {
BleManager manager = BleManager.getInstance(mContext);
manager.setDevice(bluetoothDevice);
}
if (leScanInterface != null) {
leScanInterface.onMyLeScan(bluetoothDevice, i, bytes);
}
}
public void setOnLeScan(LeScanInterface leScanInterface) {
this.leScanInterface = leScanInterface;
}
/**
* 蓝牙扫描回调接口
*/
public interface LeScanInterface {
/**
* 蓝牙扫描回调
* @param bluetoothDevice
* @param i
* @param bytes
*/
void onMyLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes);
}
}
Android5.0以后将蓝牙搜索封装成独立的对象,新的写法
BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(mScanCallback);
private ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult results) {
super.onScanResult(callbackType, results);
BluetoothDevice device = results.getDevice();
if (!devices.contains(device)) { //判断是否已经添加
devices.add(device);//也可以添加devices.getName()到列表,这里省略 }
// callbackType:回调类型
// result:扫描的结果,不包括传统蓝牙 }
};
支持不同版本的写法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
...
//定义的低于android5.0扫描方法
startScan()
} else {
...
//定义的高于android5.0扫描方法
startLeScan()
}
建立蓝牙连接
/**
* 连接蓝牙设备
*
* @param auto
* @param mGattCallback
* @return
*/
public BluetoothGatt connectDevice(boolean auto, BluetoothGattCallback mGattCallback) {
bleGatt = device.connectGatt(mContext, auto, mGattCallback);
Log.e(TAG, "生成BluetoothGatt----->" + this.bleGatt);
return bleGatt;
}
获取device
/**
* 通过mac地址直接得到BluetoothDevice
*
* @param mac
* @return
*/
public BluetoothDevice getRemoteDevice(String mac) {
device = bleAdapter.getRemoteDevice(mac);
return device;
}
继承BluetoothGattCallback类,复写相关的方法
public class BleGattCallback extends BluetoothGattCallback {
private static final String TAG = BleGattCallback.class.getName();
private Context mContext;
private CharacterInterface characterInterface;
private BleStateInterface bleStateInterface;
public BleGattCallback(Context mContext) {
this.mContext = mContext;
}
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "成功建立蓝牙通道");
if (newState == BluetoothProfile.STATE_CONNECTED) {
//发现服务
gatt.discoverServices();
Log.e(TAG, "蓝牙连接成功!");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.e(TAG, "蓝牙连接断开!");
}
if (bleStateInterface != null) {
bleStateInterface.onConnectedChange(newState);
}
} else {
Log.e(TAG, "建立蓝牙通道失败");
if (bleStateInterface != null) {
bleStateInterface.onBleGattStatus(status);
}
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
BleManager.getInstance(mContext).enableNotification(gatt, true);
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic
characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
BleManager.getInstance(mContext).writeData();
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic
characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
if (characterInterface != null) {
characterInterface.notifyChanged(gatt, characteristic);
}
}
public void setOnCharacterInterface(CharacterInterface characterInterface) {
this.characterInterface = characterInterface;
}
public void setOnConnectedChange(BleStateInterface bleStateInterface) {
this.bleStateInterface = bleStateInterface;
}
public interface CharacterInterface {
/**
* 特征值改变通知
*
* @param gatt
* @param characteristic
*/
void notifyChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
}
public interface BleStateInterface {
/**
* 蓝牙连接状态,注意回调方法不在主线程
*
* @param newState
*/
void onConnectedChange(int newState);
/**
* 蓝牙通道建立状态回调,失败的话需要关闭BlueToothGatt和BlueToothService,然后重新调用蓝牙重连
*
* @param status
*/
void onBleGattStatus(int status);
}
}
封装成完整的类
public class BleManager {
private BluetoothAdapter bleAdapter;
private BluetoothManager bleManager;
private BluetoothGatt bleGatt;
private BluetoothGattService gattService;
private BluetoothDevice device;
private Context mContext;
private static volatile BleManager singleton;
private final int REQUEST_ENABLE_BT = 1;
private StringBuffer buffer = new StringBuffer();
private String TAG = getClass().getName();
private BleManager(Context mContext) {
this.mContext = mContext;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
bleManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
bleAdapter = bleManager.getAdapter();
} else {
bleAdapter = BluetoothAdapter.getDefaultAdapter();
}
}
/**
* 获取蓝牙管理类的单例
*
* @param context
* @return
*/
public static BleManager getInstance(Context context) {
if (singleton == null) {
synchronized (BleManager.class) {
if (singleton == null) {
singleton = new BleManager(context);
}
}
}
return singleton;
}
/**
* 判断是否支持蓝牙设备
*
* @return
*/
public boolean checkIfSupportBle() {
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
/**
* 开启蓝牙设备
*/
public void enableBluetooth(Activity activity) {
if (bleAdapter == null || !bleAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
public void enableBluetooth() {
if (bleAdapter == null || !bleAdapter.isEnabled()) {
bleAdapter.enable();
}
}
/**
* 获取蓝牙绑定了的列表设备
*/
public List<BluetoothDevice> getBoundDevices() {
List<BluetoothDevice> devices = new ArrayList<>();
Set<BluetoothDevice> boundDevices = bleAdapter.getBondedDevices();
for (BluetoothDevice device : boundDevices) {
//对device进行其他操作,比如连接等。
devices.add(device);
}
return devices;
}
/**
* 通用蓝牙扫描方法,持续时间大概10s,扫描到蓝牙发出广播进行接收
*/
public void startDiscover() {
bleAdapter.startDiscovery();
}
/**
* 该方法目前稳定
*
* @param leScanCallback
*/
public void startLeScan(BluetoothAdapter.LeScanCallback leScanCallback) {
bleAdapter.startLeScan(leScanCallback);
}
/**
* 通过UUID过滤筛选出合适的蓝牙设备
*
* @param uuids
* @param leScanCallback
*/
public void startLeScan(UUID[] uuids, BluetoothAdapter.LeScanCallback leScanCallback) {
bleAdapter.startLeScan(uuids, leScanCallback);
}
/**
* 代替果实的startLeScan(),但是不稳定
*
* @param scanCallback
*/
@RequiresApi (api = Build.VERSION_CODES.LOLLIPOP)
public void startBleScan(ScanCallback scanCallback) {
bleAdapter.getBluetoothLeScanner().startScan(scanCallback);
}
/**
* 关闭蓝牙搜索
*
* @param leScanCallback
*/
public void stopLeScan(BluetoothAdapter.LeScanCallback leScanCallback) {
bleAdapter.stopLeScan(leScanCallback);
}
/**
* 蓝牙是否开启
*
* @return
*/
public boolean isOpen() {
return bleAdapter.isEnabled();
}
/**
* 关闭蓝牙设备
*
* @param scanCallback
*/
@RequiresApi (api = Build.VERSION_CODES.LOLLIPOP)
public void stopBleScan(ScanCallback scanCallback) {
bleAdapter.getBluetoothLeScanner().stopScan(scanCallback);
}
public void setBleGatt(BluetoothGatt bleGatt) {
this.bleGatt = bleGatt;
}
public void setGattService(BluetoothGattService gattService) {
this.gattService = gattService;
}
public BluetoothGatt getBleGatt() {
return bleGatt;
}
public BluetoothGattService getGattService() {
return gattService;
}
public BluetoothDevice getDevice() {
return device;
}
public void setDevice(BluetoothDevice device) {
this.device = device;
}
/**
* 开启蓝牙设备
*/
public void openBle() {
bleAdapter.enable();
}
/**
* 关闭蓝牙设备
*/
public void closeBle() {
bleAdapter.disable();
}
/**
* 通过mac地址直接得到BluetoothDevice
*
* @param mac
* @return
*/
public BluetoothDevice getRemoteDevice(String mac) {
device = bleAdapter.getRemoteDevice(mac);
return device;
}
/**
* 连接蓝牙设备
*
* @param auto
* @param mGattCallback
* @return
*/
public BluetoothGatt connectDevice(boolean auto, BluetoothGattCallback mGattCallback) {
bleGatt = device.connectGatt(mContext, auto, mGattCallback);
Log.e(TAG, "生成BluetoothGatt----->" + this.bleGatt);
return bleGatt;
}
/**
* 获取对应应用的服务
* @return
*/
public BluetoothGattService getDefaultGattService() {
this.gattService = bleGatt.getService(UUID.fromString(UUIDManager.SERVICE_UUID));
return gattService;
}
@SuppressLint ("NewApi")
public boolean enableNotification(BluetoothGatt bluetoothGatt, boolean enable) {
if (gattService == null) {
getDefaultGattService();
}
BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(UUID.fromString(UUIDManager.NOTIFY_UUID));
if (bluetoothGatt == null || characteristic == null) {
return false;
}
if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable)) {
return false;
}
//获取到Notify当中的Descriptor通道 然后再进行注册
BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUID.fromString(UUIDManager.NOTIFY_DESCRIPTOR));
if (clientConfig == null) {
return false;
}
if (enable) {
clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
} else {
clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
}
return bluetoothGatt.writeDescriptor(clientConfig);
}
/**
* 蓝牙设备传数据
*
* @param data
* @return
*/
private boolean writeBluetoothData(String data) {
BluetoothGattCharacteristic writeCharacter = null;
if (gattService == null) {
gattService = getDefaultGattService();
if (gattService == null) {
return false;
}
}
writeCharacter = gattService.getCharacteristic(UUID.fromString(UUIDManager.WRITE_UUID));
// 设置监听
this.bleGatt.setCharacteristicNotification(writeCharacter, true);
// 当数据传递到蓝牙之后
// 会回调BluetoothGattCallback里面的write方法
writeCharacter.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
// 将需要传递的数据 打碎成16进制
try {
writeCharacter.setValue(CommonUtils.getHexBytes(data));
} catch (Exception e) {
e.printStackTrace();
}
return this.bleGatt.writeCharacteristic(writeCharacter);
}
/**
* 向蓝牙设备写入数据
*/
public void writeData(String content) {
buffer.append(content);
writeData();
}
public void writeData() {
int length = buffer.length();
String writeData = "";
if (length >= 20) {
writeData = buffer.substring(0, 20);
} else {
writeData = buffer.toString();
}
if (writeData.length() > 0) {
writeBluetoothData(writeData);
}
if (writeData.length() > 0) {
buffer.delete(0, writeData.length());
}
}
/**
* 关闭蓝牙连接
*/
public void closeGatt() {
if (bleGatt != null) {
bleGatt.disconnect();
bleGatt.close();
Log.e(TAG, "连接蓝牙断开bleGatt" + this.bleGatt);
Log.e(TAG, "连接蓝牙断开gattService" + this.gattService);
bleGatt = null;
gattService = null;
}
}
}
UUID的获取
一般而言UUID会由硬件工程师提供的,但是有时候硬件工程师也不知道,这时就需要自己去检测了。首先通过寻找服务Service的UUID,然后通过找出特征值的UUID,并且判断特征值的UUID的属性是读,写还是通知。也可以可以借助引用nRF Connect连接蓝牙得到相应的值。
/**
* 展示服务Services和characteristic对应的UUID,以及具备的属性。
*
* @param gattServices
*/
public boolean queryGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null) {
return false;
}
for (BluetoothGattService myService : gattServices) {
//找出服务的UUID
String uuId = myService.getUuid().toString();
List<BluetoothGattCharacteristic> gattCharacteristics =myService.getCharacteristics();
for (final BluetoothGattCharacteristic gattCharacteristic: gattCharacteristics) {
Log.e(TAG,"---->char uuid:"+gattCharacteristic.getUuid());
int charaProp = gattCharacteristic.getProperties();
//所有Characteristics按属性分类
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
Log.d(TAG, "gattCharacteristic的UUID为:" + gattCharacteristic.getUuid());
Log.d(TAG, "gattCharacteristic的属性为: 可读");
}
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
Log.d(TAG, "gattCharacteristic的UUID为:" + gattCharacteristic.getUuid());
Log.d(TAG, "gattCharacteristic的属性为: 可写");
}
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
Log.d(TAG, "gattCharacteristic的UUID为:" + gattCharacteristic.getUuid() + gattCharacteristic);
Log.d(TAG, "gattCharacteristic的属性为: 具备通知属性");
}
}
return false;
}
}
这样就能找到对应服务的UUID和特征值characteristic的UUID,从而进行相应的读写和通知的操作。读写通知有可能公用一个通道,即特征值UUID有可能相同,但是这不影响通信。对于外部蓝牙硬件,一般通过通知的方式,接收返回的数据,开启蓝牙通知的方式为BleManager的enableNotification()方法。
补充蓝牙读的写法:
bleGatt.readCharacteristic(characteristic);
如果要使用,放在BluetoothGattCallback复写方法onCharacteristicRead()方法下进行逻辑操作。