本文主要讲解BLE(低功耗蓝牙4.0以上)的使用和封装,为了UI层方便拿取数据展示,统一对蓝牙搜索、连接、数据交互、蓝牙协议等封装为lib。
为什么要学习蓝牙技术,蓝牙作为一种成熟、低功耗无线通信技术的先锋,在可穿戴设备领域中扮演着越来越重要的作用。
BLE分为三部分:Service,Characteristic,Descriptor。这三部分都是使用UUID来作为唯一标识符加以区分。一个BLE终端可以包含多个Service,一个Service可以包含多个Characteristic,而一个Characteristic包含一个value和多个Descriptor,一个Descriptor只包含一个value。UUID格式为:0000ffe1-0000-1000-8000-00805f9b34fb
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
如果你不确定你的app使用的设备是否支持低功耗蓝牙,但又想让支持的设备使用低功耗蓝牙,把标志位改为false,同时去代码中判断设备是否支持BLE:
// 是否支持蓝牙低功耗广播(4.3+)
if (!getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
finish()//此处可以根据特定需求去改变处理
return false;
}
有两种获取方式:
1).BluetoothManager bluetoothManager =(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
2).BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
此处需要做非空判断,为null则表示设备不支持蓝牙。
说明:startDiscovery()方法在大多数手机上是可以同时发现经典蓝牙和低功耗蓝牙(BLE)的,但是startDiscovery()的回调无法返回BLE的广播,所以无法通过广播识别设备,而且startDiscovery()扫描BLE效率比startLeScan()低很多。因此需要根据具体的需求去做适配,才能更高效的搜寻蓝牙。PS: startLeScan()和startScan()有重载方法可以指定规则,参数去搜索。
获取方式:
BluetoothGatt mBluetoothGatt = device.connectGatt(getContext(), false, mGattCallback);//创建连接
与BLE蓝牙交互分为三步骤:搜索、连接、读写数据。BLE蓝牙无需配对即可连接
在搜索、连接蓝牙之前,首先对官方提供的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 getSupportedGattServices() {
if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getServices();
}
/**
* 获取RSSI值
*
* @return null false
*/
public boolean getRssiValue() {
return mBluetoothGatt != null
&& mBluetoothGatt.readRemoteRssi();
}
/**
* 获取蓝牙设备对象
*
* @return BluetoothDevice
*/
public BluetoothDevice getDevice() {
return mBluetoothDevice;
}
/**
* 判断设备是否连接
*
* @param address 设备地址
* @return true 已连接
*/
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Logger.e("蓝牙适配器没有初始化获取mac地址未指明。");
return false;
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Logger.e("蓝牙设备没有发现,无法连接。");
return false;
}
close();//每次连接之前先释放掉原来的资源
mBluetoothGatt = device.connectGatt(getContext(), false, mGattCallback);//创建新的连接
return true;
}
/**
* 断开连接
*/
public void disconnect() {
if (mBluetoothGatt != null) mBluetoothGatt.disconnect();
}
/**
* 释放资源
*/
public void close() {
if (mBluetoothGatt != null) {
mBluetoothGatt.close();
mBluetoothDevice = null;
}
}
/**
* 蓝牙协议回调
*/
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
/**
* 连接状态
*/
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (mStateChangeListener == null) return;
if (newState == BluetoothProfile.STATE_CONNECTED) {// 连接状态
mStateChangeListener.connected(gatt.getDevice());
setConnected(true);
mBluetoothDevice = gatt.getDevice();
discoverServices();//设备连接成功,查找服务!
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {// 断开连接
close();
mStateChangeListener.disconnected();
setConnected(false);
mBluetoothDevice = null;
}
}
/**
* 是否发现服务
*/
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
if (mCharacteristicListener != null)
mCharacteristicListener.onServicesDiscovered();
} else {
Logger.e("蓝牙通信服务回调失败:status=" + status);
}
}
/**
* 读操作回调
*/
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (mCharacteristicListener != null) {
mCharacteristicListener.onCharacteristicRead(gatt, characteristic, status);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (mCharacteristicListener != null) {
mCharacteristicListener.onCharacteristicChanged(gatt, characteristic);
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (mCharacteristicListener != null) {
mCharacteristicListener.onCharacteristicWrite(gatt, characteristic, status);
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Logger.e("onDescriptorWrite: status=" + status + " uuid=" + descriptor.getUuid().toString());
}
/**
* 信号强度
*/
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
if (mCharacteristicListener != null) {
mCharacteristicListener.onReadRemoteRssi(gatt, rssi, status);
}
}
};
/**
* 连接状态接口
*/
public interface OnConnectionStateChangeListener {
void connected(BluetoothDevice device);
void disconnected();
}
/**
* 发现服务、数据读写操作接口
*/
public interface OnCharacteristicListener {
void onServicesDiscovered();
void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status);
void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status);
void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status);
}
private OnConnectionStateChangeListener mStateChangeListener = null;
private OnCharacteristicListener mCharacteristicListener = null;
public void setOnConnectionStateChangeListener(OnConnectionStateChangeListener listener) {
this.mStateChangeListener = listener;
}
public void setOnCharacteristicListener(OnCharacteristicListener listener) {
this.mCharacteristicListener = listener;
}
}
定义IBluzScanHelper接口,同时在接口中定义搜索和连接蓝牙需要使用到的方法,统一化管理。
package com.csym.bluetoothlib.helper;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
/**
* 蓝牙设备搜索连接管理类
* Created by ${zhoupeng} on 2016/8/9.
*/
public interface IBluzScanHelper {
/**
* 注册蓝牙回调广播
*
* @param context 上下文
*/
void registBroadcast(Context context);
/**
* 注销蓝牙回调广播
*
* @param context 上下文
*/
void unregistBroadcast(Context context);
/**
* 获取设备使能信息
*/
boolean isEnabled();
/**
* 关闭蓝牙
*/
boolean disable();
/**
* 打开蓝牙
*/
boolean enable();
/**
* 打开蓝牙提示框
*/
void openBluetooth();
/**
* 开始扫描蓝牙设备
*/
void startDiscovery();
/**
* 取消扫描
*/
void cancelDiscovery();
/**
* 连接蓝牙设备,当前应用所需的Profile
*/
boolean connect(BluetoothDevice device);
/**
* 断开全部连接
*/
void disconnect();
/**
* 设置搜索设备回调监听
*/
void addOnDiscoveryListener(OnDiscoveryListener listener);
void removeOnDiscoveryListener(OnDiscoveryListener listener);
/**
* 设置连接回调监听
*/
void addOnConnectionListener(OnConnectionListener listener);
void removeOnConnectionListener(OnConnectionListener listener);
/**
* 断开数据连接,释放资源
*/
void release();
/**
* 获取当前已连接的设备
*/
BluetoothDevice getConnectedDevice();
/**
* 查找蓝牙回调接口
*/
interface OnDiscoveryListener {
/**
* 找到设备
*/
void onFound(BluetoothDevice device);
/**
* 搜索结束
*/
void onDiscoveryFinished();
}
/**
* 连接回调接口
*/
interface OnConnectionListener {
/**
* 已连接
*/
void onConnected(BluetoothDevice device);
/**
* 已断开连接
*/
void onDisconnected(BluetoothDevice device);
}
}
建立BluzScanHelper帮助类,对IBluzScanHelper接口中的方法具体实现,同时提供内部接口将搜索到的蓝牙设备和连接状态提供出去。以下主要讲解下搜索方法的具体实现:
(1) api21以下ble搜索方法为:
bluetoothAdapter.startLeScan(mLeScanCallback);
此方法如果不主动停止将会一直搜索,比较消耗cpu,因此在开启搜索的同时开启一个定时为10秒的定时器去关闭搜索,如果有需要可以在上文提到的onDiscoveryFinished()搜索结束回调接口当中间隔一段时间继续开启搜索,直到连接上你需要的设备为止。
(2) api21以上ble搜索方法为:
//使用之前需要判断当前系统蓝牙是否可用
if (!bluetoothAdapter.isEnabled()) return;
mLeScanner = bluetoothAdapter.getBluetoothLeScanner();
mLeScanner.startScan(mBleScanCallback);
在不同的版本上需使用不同的方法去搜索,如果在低版本上使用高版本的方法,会造成程序的奔溃。以上两种方法在查找到需要的设备同时都应该取消搜索,释放此资源。
以下为BluzScanHelper类:
package com.csym.bluetoothlib.helper;
import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.support.annotation.RequiresApi;
import com.csym.bluetoothlib.BluetoothLeService;
import com.csym.bluetoothlib.utils.SharePreferenceUtils;
import com.orhanobut.logger.Logger;
import java.util.ArrayList;
import java.util.List;
/**
* 蓝牙设备搜索连接管理类
* Created by ${zhoupeng} on 2016/8/9.
*/
public class BluzScanHelper implements IBluzScanHelper, BluetoothLeService.OnConnectionStateChangeListener {
private BluetoothLeService mBluetoothLeService;//蓝牙服务类
private BluetoothAdapter bluetoothAdapter;//蓝牙适配器
private BluetoothLeScanner mLeScanner = null;//api 5.0版本以上新的低功耗蓝牙搜索类
private BleScanCallback mBleScanCallback = null;//api 5.0版本以上搜索回调接口
private static IBluzScanHelper INSTANCE = null;
private Activity mActivity = null;
public Activity getActivity() {
return mActivity;
}
private boolean isDiscovery = false;
private boolean isDiscovery() {
return isDiscovery;
}
private void setDiscovery(boolean discovery) {
isDiscovery = discovery;
}
private static final int MSG_CONNECTED = 1;//连接
private static final int MSG_DISCONNECTED = 2;//未连接
/**
* 10秒后停止查找搜索.
*/
private static final int SCAN_PERIOD = 10 * 1000;
private SharePreferenceUtils preferenceUtils = null;
/**
* 使用handler返回主线程,避免UI层直接操作而导致的奔溃
*/
private Handler mHandler = new Handler(new Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_CONNECTED://连接
BluetoothDevice device = (BluetoothDevice) msg.obj;
if (connectionListenerList != null && connectionListenerList.size() > 0 && device != null) {
for (int i = 0; i < connectionListenerList.size(); i++) {
connectionListenerList.get(i).onConnected(device);
}
}
break;
case MSG_DISCONNECTED://未连接
for (int i = 0; i < connectionListenerList.size(); i++) {
connectionListenerList.get(i).onDisconnected(null);
}
break;
}
return false;
}
});
public static IBluzScanHelper getInstance(Activity activity) {
if (INSTANCE == null) {
synchronized (BluetoothLeService.class) {
if (INSTANCE == null) {
INSTANCE = new BluzScanHelper(activity);
}
}
}
return INSTANCE;
}
private BluzScanHelper(Activity activity) {
this.mActivity = activity;
//蓝牙服务
mBluetoothLeService = BluetoothLeService.getInstance(activity);
mBluetoothLeService.setOnConnectionStateChangeListener(this);
//蓝牙适配器
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter != null
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mLeScanner = bluetoothAdapter.getBluetoothLeScanner();
mBleScanCallback = new BleScanCallback();
}
//SharePreference保存蓝牙断开行为是主动断开还是自动断开
preferenceUtils = SharePreferenceUtils.getInstance(activity);
}
@Override
public void registBroadcast(Context context) {
Logger.e("注册监听系统蓝牙状态变化广播 ");
IntentFilter filter = new IntentFilter();
//蓝牙状态改变action
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
//蓝牙断开action
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
context.registerReceiver(receiver, filter);
}
@Override
public void unregistBroadcast(Context context) {
try {
if (receiver != null) context.unregisterReceiver(receiver);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
@Override
public boolean isEnabled() {
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
}
@Override
public boolean disable() {
return bluetoothAdapter != null && bluetoothAdapter.disable();
}
@Override
public boolean enable() {
return bluetoothAdapter != null && bluetoothAdapter.enable();
}
@Override
public void openBluetooth() {
//打开蓝牙提示框
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
getActivity().startActivityForResult(enableBtIntent, 100);
}
@Override
public void startDiscovery() {
if (bluetoothAdapter == null) {
Logger.e("开始搜索蓝牙失败,蓝牙适配器为null");
return;
}
Logger.e("开始搜索蓝牙 isDiscovering=" + bluetoothAdapter.isDiscovering());
//正在查找中,不做处理
if (isDiscovery()) return;
setDiscovery(true);
//判断版本号,如果api版本号大于5.0则使用最新的方法搜素
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
scanLeDevice(true);
} else {
if (!bluetoothAdapter.isEnabled()) return;
mLeScanner = bluetoothAdapter.getBluetoothLeScanner();
mLeScanner.startScan(mBleScanCallback);
}
}
@Override
public void cancelDiscovery() {
if (bluetoothAdapter == null) {
Logger.e("取消搜索蓝牙失败,蓝牙适配器为null");
return;
}
// if (!isDiscovery()) {
// Logger.e("取消搜索蓝牙失败,当前状态为取消状态!");
// return;
// }
setDiscovery(false);//复位正在查找标志位
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
scanLeDevice(false);
} else {
stopLeScan();
}
Logger.e("取消搜索蓝牙 isDiscovering=" + bluetoothAdapter.isDiscovering());
}
/**
* 低功耗蓝牙开始查找蓝牙
*
* @param enable 是否开始查找
*/
private void scanLeDevice(boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
stopLeDevice();
}
}, SCAN_PERIOD);
bluetoothAdapter.startLeScan(mLeScanCallback);
} else {
stopLeDevice();
}
}
/**
* 低功耗蓝牙停止查找
*/
private void stopLeDevice() {
bluetoothAdapter.stopLeScan(mLeScanCallback);
for (int i = 0; i < discoveryListenerList.size(); i++) {
discoveryListenerList.get(i).onDiscoveryFinished();
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void stopLeScan() {
if (!bluetoothAdapter.isEnabled()) return;
mLeScanner = bluetoothAdapter.getBluetoothLeScanner();
mLeScanner.stopScan(mBleScanCallback);
for (int i = 0; i < discoveryListenerList.size(); i++) {
discoveryListenerList.get(i).onDiscoveryFinished();
}
}
@Override
public boolean connect(BluetoothDevice device) {
if (device == null || mBluetoothLeService == null) return false;
cancelDiscovery();//取消搜索
return mBluetoothLeService.connect(device.getAddress());
}
@Override
public void disconnect() {
if (mBluetoothLeService != null) {
mBluetoothLeService.disconnect();
preferenceUtils.save(SharePreferenceUtils.SHARE_REFRESH_IS_MANUAL, true);
}
}
@Override
public void connected(BluetoothDevice device) {
Message message = new Message();
message.obj = device;
message.what = MSG_CONNECTED;
mHandler.sendMessage(message);
preferenceUtils.save(SharePreferenceUtils.SHARE_REFRESH_IS_MANUAL, false);
cancelDiscovery();//连接成功后取消搜索
}
@Override
public void disconnected() {
mHandler.sendEmptyMessage(MSG_DISCONNECTED);
}
@Override
public void release() {
if (mBluetoothLeService != null) {
mBluetoothLeService.disconnect();
mBluetoothLeService.close();
}
}
@Override
public BluetoothDevice getConnectedDevice() {
return mBluetoothLeService.getDevice();
}
private List discoveryListenerList = new ArrayList<>();//查找蓝牙回调接口集合
private List connectionListenerList = new ArrayList<>();//连接蓝牙回调接口集合
@Override
public void addOnDiscoveryListener(OnDiscoveryListener listener) {
if (!discoveryListenerList.contains(listener))
discoveryListenerList.add(listener);
}
@Override
public void removeOnDiscoveryListener(OnDiscoveryListener listener) {
discoveryListenerList.remove(listener);
}
@Override
public void addOnConnectionListener(OnConnectionListener listener) {
connectionListenerList.add(listener);
}
@Override
public void removeOnConnectionListener(OnConnectionListener listener) {
connectionListenerList.remove(listener);
}
/**
* 查找低功耗蓝牙接口回调
*/
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
//注意在此方法中不要过多的操作
if (device != null && discoveryListenerList != null) {
for (int i = 0; i < discoveryListenerList.size(); i++) {
discoveryListenerList.get(i).onFound(device);
}
}
}
};
/**
* api21+低功耗蓝牙接口回调,以下回调的方法可以根据需求去做相应的操作
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private class BleScanCallback extends ScanCallback {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (result == null) return;
BluetoothDevice device = result.getDevice();
if (device != null && discoveryListenerList != null) {
for (int i = 0; i < discoveryListenerList.size(); i++) {
discoveryListenerList.get(i).onFound(device);
}
}
}
@Override
public void onBatchScanResults(List results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
}
}
/**
* 查找蓝牙广播回调
*/
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Logger.e("蓝牙广播回调 action=" + action);
if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_OFF) {//关闭系统蓝牙
setDiscovery(false);
Logger.e("系统蓝牙断开!!");
boolean isEnable = enable();
if (!isEnable) openBluetooth();
} else if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_ON) {//系统蓝牙打开
setDiscovery(false);
Logger.e("系统蓝牙打开!!");
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
startDiscovery();
}
}, 500);
}
}
};
}
新建BluzManager类,主要功能:
(1)管理蓝牙服务协议。与设备蓝牙连接成功后,调用discoverServices()方法回调蓝牙服务协议,再根据底层提供的协议对蓝牙服务进行过滤,过滤出需要的、可用的特征服务和特征值并缓存。
(2)读写、通知数据回调处理。在对设备进行读、写、通知操作时,会有相应的数据回调回来。示例当中创建了解析工厂模式对通知回来的数据进行了对应的处理,并以广播的形式发送出去。
(3)通信管理。在此类中创建方法分别实现底层操作指令的拼接,上层如果有需要,直接调用相关方法即可。
package com.csym.bluetoothlib.manager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import com.csym.bluetoothlib.BluetoothAttributes;
import com.csym.bluetoothlib.BluetoothLeService;
import com.csym.bluetoothlib.decoder.IDecoder;
import com.csym.bluetoothlib.factory.DecoderFactory;
import com.csym.bluetoothlib.thread.WriteThread;
import com.csym.bluetoothlib.utils.CRCUtils;
import com.csym.bluetoothlib.utils.HexStringUtils;
import com.orhanobut.logger.Logger;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import static android.bluetooth.BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
/**
* 设备管理类
* Created by ${zhoupeng} on 2016/8/11.
*/
public class BluzManager implements BluetoothLeService.OnCharacteristicListener {
private static BluzManager INSTANCE = null;
private Context mContext;
private BluetoothLeService mBluetoothLeService;
/**
* 缓存连接设备的蓝牙特征值和uuid
*/
private HashMap mWriteHashMap = new HashMap<>();
private WriteThread mWriteThread;
private DecoderFactory mDecoderFactory;
private Context getContext() {
return mContext;
}
/**
* 蓝牙信号强度回调接口
*/
private OnReadRemoteRssiListener listener;
public interface OnReadRemoteRssiListener {
void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status);
}
public void setOnReadRemoteRssiListener(OnReadRemoteRssiListener listener) {
this.listener = listener;
}
private BluzManager(Context context) {
mContext = context;
initialize();
}
/**
* 单列模式,确保唯一性
*
* @param context 上下文
* @return BluzManager蓝牙管理类
*/
public static BluzManager getInstance(Context context) {
if (INSTANCE == null) {
synchronized (BluzManager.class) {
if (INSTANCE == null) {
INSTANCE = new BluzManager(context);
}
}
}
return INSTANCE;
}
/**
* 初始化相关数据
*/
private void initialize() {
//初始化蓝牙服务类
mBluetoothLeService = BluetoothLeService.getInstance(getContext());
mBluetoothLeService.setOnCharacteristicListener(this);
//初始化解析工厂
if (mDecoderFactory == null) mDecoderFactory = new DecoderFactory(getContext());
//初始化写线程
if (mWriteThread == null) mWriteThread = new WriteThread(mBluetoothLeService);
}
/**
* 获取蓝牙信号强度,只能单次获取
*
* @return boolean
*/
public boolean getRssiValue() {
return mBluetoothLeService.getRssiValue();
}
/**
* 清楚所有缓存数据
*/
private void clearHashMap() {
mWriteHashMap.clear();
}
/**
* 蓝牙服务发现回调
*/
@Override
public void onServicesDiscovered() {
clearHashMap();
List list = mBluetoothLeService.getSupportedGattServices();
for (BluetoothGattService bluetoothGattService : list) {
UUID uuid = bluetoothGattService.getUuid();
Logger.d("蓝牙服务回调 uuid=" + uuid.toString());
//根据底层提供的可用的特征服务uuid过滤出可用的服务以及特征值
if (BluetoothAttributes.UUID_CHARACTERISTICS_SERVICE.equalsIgnoreCase(uuid.toString())) {
List characteristicList = bluetoothGattService.getCharacteristics();
for (BluetoothGattCharacteristic characteristic : characteristicList) {
//根据服务特征中的属性区分是可读、可写、通知。
int properties = characteristic.getProperties();
//拥有写权限的uuid放入集合中缓存起来,在需要使用的时候拿取出来。
if ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) {
mWriteHashMap.put(characteristic.getUuid(), characteristic);
}
//打开通知权限,以下BluetoothAttributes.UUID_RESPONSE_2902为举例说明,具体根据底层给过来的文档去修改
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
if (mBluetoothLeService.setCharacteristicNotification(characteristic, true)) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(BluetoothAttributes.UUID_RESPONSE_2902));
descriptor.setValue(ENABLE_NOTIFICATION_VALUE);
mBluetoothLeService.writeDescriptor(descriptor);
}
}
}
}
}
//如果缓存特征服务为空,表示服务回调失败了,可以尝试断开连接或者关闭系统蓝牙重新去连接。
if (mWriteHashMap.size() == 0) {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter != null) bluetoothAdapter.disable();
return;
}
//初始化写数据线程特征服务
mWriteThread.initData(mWriteHashMap);
//获取服务成功则允许数据写入,,断开连接则重置
mWriteThread.setServiceCallback(true);
Logger.e("蓝牙服务回调:mWriteHashMap=" + mWriteHashMap);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Logger.e("蓝牙数据读取回调 status=" + status);
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
BluetoothDevice device = mBluetoothLeService.getDevice();//当前连接的蓝牙对象
if (characteristic == null || characteristic.getValue() == null || device == null) return;
String data = HexStringUtils.bytesToHexString(characteristic.getValue());//将byte类型数据转换为16进制
Logger.e("蓝牙数据通知回调 uuid==" + characteristic.getUuid().toString() + ",data=" + data);
// 工厂模式分别去解析数据
IDecoder decoder = mDecoderFactory.getDecoder(data);
if (decoder == null) return;
decoder.decode(data);
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Logger.e("蓝牙数据写入回调 status=" + status);
if (status == BluetoothGatt.GATT_SUCCESS) {//数据写入成功,写入下一条指令
mWriteThread.threadNotify(true);
} else {
mWriteThread.threadNotify(false);
}
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
if (listener != null) listener.onReadRemoteRssi(gatt, rssi, status);
}
/**
* 示例:时间同步
* 根据实际需求去改变
*/
public void timeSynch() {
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int dayMon = calendar.get(Calendar.DAY_OF_MONTH);
int week = calendar.get(Calendar.DAY_OF_WEEK);
int dayWeek = week == 1 ? 6 : week - 2;
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
byte[] content = new byte[10];
content[0] = (byte) HexStringUtils.hexStringToAlgorism("c0");//头字段
content[1] = (byte) 0x0a;//数据长度
content[2] = HexStringUtils.str2Bcd((year % 100) + "")[0];//年 L
content[3] = HexStringUtils.str2Bcd((year / 100) + "")[0];//年 H
content[4] = HexStringUtils.str2Bcd(month + "")[0];//月
content[5] = HexStringUtils.str2Bcd(dayMon + "")[0];//月--日
content[6] = HexStringUtils.str2Bcd(dayWeek + "")[0];//星期---日
content[7] = HexStringUtils.str2Bcd(hour + "")[0];//小时
content[8] = HexStringUtils.str2Bcd(minute + "")[0]; //分钟
content[9] = HexStringUtils.str2Bcd(second + "")[0];//秒
mWriteThread.startWrite(getCrc16(content));
}
/**
* 添加校验码
*
* @param content byte[]
* @return 具备校验码的byte[]
*/
private byte[] getCrc16(byte[] content) {
int crc = CRCUtils.crc16_ccitt(content, content.length);//crc校验码
byte crcL = (byte) (crc & 0xff);
byte crcH = (byte) ((crc >> 8) & 0xff);
byte[] bytes = new byte[content.length + 2];
System.arraycopy(content, 0, bytes, 0, content.length);
bytes[content.length] = crcL;
bytes[content.length + 1] = crcH;
return bytes;
}
/**
* 停止写入数据
*/
public void stopWrite() {
if (mWriteThread != null) {
mWriteThread.stopWrite();
mWriteThread = null;
}
}
/**
* 蓝牙断开,清除剩余指令
*/
public void clearOrder() {
if (mWriteThread != null) {
mWriteThread.setServiceCallback(false);
mWriteThread.clearAll();
}
}
}
上文所用到的数据转换,线程发送命令等工具类在源码中都有体现。