当前位置: 代码迷 >> 综合 >> 蓝牙BLE工具
  详细解决方案

蓝牙BLE工具

热度:91   发布时间:2024-02-29 11:23:09.0

使用注意事项:

1.单例模式,直接getInstance()获取对象。

2.使用前需要init()。

3.回调分为两部分,一部分扫描,一部分连接:

public void setOnBleCallback(OnBleCallback onBleCallback)
public void setOnBleConnectCallback(OnBleCallback onBleConnectCallback)

4.读写间隔:

读写操作都是队列操作,需要等待操作结果返回后,才能进行下次操作,若当次操作未完成,下次操作调用时,将直接返回操作启用失败。

写入操作时,需等待服务器的确认信息,即写入回调,再进行下次写入操作。

当写入类型设置为 不需要接收服务器确认信息(PROPERTY_WRITE_NO_RESPONSE)以加快传输速度时,两次操作之间应保留 80ms ~ 100ms 或以上的延时。

5.权限,主要为蓝牙开关权限和定位权限,高版本记得动态申请:

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

6.动态申请:

/*** 检查蓝牙权限和定位权限*/
private void checkPermission() {PermissionX.init(this).permissions(Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.BLUETOOTH_ADMIN).onExplainRequestReason(new ExplainReasonCallback() {@Overridepublic void onExplainReason(ExplainScope scope, List<String> deniedList) {scope.showRequestReasonDialog(deniedList,"你需要同意以下权限才能正常使用","确认","取消");}}).request(new RequestCallback() {@Overridepublic void onResult(boolean allGranted, List<String> grantedList, List<String> deniedList) {if (allGranted){Toast.makeText(mActivity,"权限已通过",Toast.LENGTH_SHORT).show();mFirstDeviceListBean.setCheckStatus(true);registerBroadcastReceiver(mActivity);BleUtils.getInstance().init(mActivity);}else {String nonePermission = deniedList.toString();Toast.makeText(mActivity,"权限未通过"+nonePermission,Toast.LENGTH_SHORT).show();}}});
}

7.监听蓝牙开关的广播:

private void registerBroadcastReceiver(Context context) {IntentFilter intentFilter = new IntentFilter();// 监视蓝牙关闭和打开的状态intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);context.registerReceiver(stateChangeReceiver, intentFilter);
}private BroadcastReceiver stateChangeReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);if (BluetoothAdapter.STATE_ON == blueState) {Log.d(TAG, "onReceive: 打开蓝牙");mFirstDeviceListBean.setBleStatus(true);mHandler.sendEmptyMessage(MSG_BLE_STATUS);} else if (BluetoothAdapter.STATE_OFF == blueState) {Log.d(TAG, "onReceive: 关闭蓝牙");mFirstDeviceListBean.setBleStatus(false);mHandler.sendEmptyMessage(MSG_BLE_STATUS);}}
};

8.正餐:

public class BleUtils {private static volatile BleUtils bleUtils;//限制产生多个对象public BleUtils() { }//通过该方法获得实例对象public static BleUtils getInstance(){if (bleUtils == null){synchronized(BleUtils.class){if (bleUtils == null){bleUtils = new BleUtils();}}}return bleUtils;}/**** 蓝牙当前状态和扫描结果的回调*/public void setOnBleCallback(OnBleCallback onBleCallback) {this.onBleCallback = onBleCallback;}private OnBleCallback onBleCallback;public interface OnBleCallback{void startScan();void stopScan();void failedScan();void onBatchScanResults(List<ScanResult> results);void onScanResult(int callbackType, ScanResult result);}/**** 必须初始化,并且检查是否支持ble*/private BluetoothManager mBluetoothManager;private BluetoothAdapter mBluetoothAdapter;public boolean init(Activity activity){mBluetoothManager = (BluetoothManager) activity.getSystemService(Context.BLUETOOTH_SERVICE);mBluetoothAdapter = mBluetoothManager.getAdapter();return mBluetoothAdapter != null;}/**** 检查蓝牙开关状态*/public boolean getBleEnable(){return mBluetoothAdapter.isEnabled();}/**** 开关蓝牙开关*/public boolean setBleEnable(boolean status){if (status){mBluetoothAdapter.enable();}else {mBluetoothAdapter.disable();}return mBluetoothAdapter.isEnabled();}/**** 开始扫描其他蓝牙设备*/public void startScanBleDevice(){mBluetoothAdapter.getBluetoothLeScanner().stopScan(scanCallback);mBluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback);if (onBleCallback != null){onBleCallback.startScan();}}/**** 停止扫描其他蓝牙设备*/public void stopScanBleDevice(){mBluetoothAdapter.getBluetoothLeScanner().stopScan(scanCallback);if (onBleCallback != null){onBleCallback.stopScan();}}/**** 扫描回调*   @param device:    识别到的远程设备*   @param rssi:      信号强度指示,计数为dB。可以通过:d = 10^((abs(RSSI) - A) / (10 * n))计算出距离。*              A和n根据环境改变,需经实验测出,给出两个网上的经验值:<1>  A: 50 n: 2.5    <2>  A: 59 n: 2.0*   @param scanRecord:广播数据和扫描应答数据数据 BLE设备在对外广播中,广播中会携带一些有用的信息。*                    其中包含了 广播数据 和 扫描应答数据, 两者有效荷载最大都为 31字节(蓝牙4),*                    以十六进制格式存储,可通过 bytesToHex 转换成可用的字符串。*   注意相同的 BluetoothDevice 会重复出现在回调中,所以如果要记录蓝牙列表,需要自行过滤重复出现的设备*/private ScanCallback scanCallback = new ScanCallback() {@Overridepublic void onScanResult(int callbackType, ScanResult result) {super.onScanResult(callbackType, result);Log.d(TAG, "onScanResult() called with: callbackType = [" + callbackType + "]," +" result = [" + result.toString() + "]");if (onBleCallback != null){onBleCallback.onScanResult(callbackType,result);}}@Overridepublic void onBatchScanResults(List<ScanResult> results) {super.onBatchScanResults(results);Log.d(TAG, "onBatchScanResults: "+results.toString());if (onBleCallback != null){onBleCallback.onBatchScanResults(results);}}@Overridepublic void onScanFailed(int errorCode) {super.onScanFailed(errorCode);Log.d(TAG, "onScanFailed: ");if (onBleCallback != null){onBleCallback.failedScan();}}};/**** 蓝牙连接或断开状态等回调*/public void setOnBleConnectCallback(OnBleCallback onBleConnectCallback) {this.onBleConnectCallback = onBleConnectCallback;}private OnBleCallback onBleConnectCallback;public interface OnBleConnectCallback{void connectSuccess();void connectFailed();}/**** 连接设备* autoConnect:自动连接,设备不可用时会不断尝试重连* callback:   BluetoothGattCallback实例,用于接收异步回调* transport:  GATT连接到双模设备的首选传输模式:*                 1:TRANSPORT_AUTO   自动选择 (默认值)*                 2:TRANSPORT_BREDR  BR/EDR 传统蓝牙*                 3:TRANSPORT_LE     LE 低耗蓝牙* handler:  传入一个Handler,以指定回调发生的线程,传入null时,回调将会在一个未指定的后台线程上进行*/public void connectDevice(Activity activity, ScanResult result){stopScanBleDevice();//先停止扫描Log.d(TAG, "connectDevice: "+result.toString());result.getDevice().connectGatt(activity,false,gattCallback);}/**** 断开设备*/public void disconnectDevice(){if (mBluetoothGatt != null){mBluetoothGatt.disconnect();mBluetoothGatt.close();mBluetoothGatt = null;}}private BluetoothGatt mBluetoothGatt;//连接的设备/**** 连接状态回调*/private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {/*** GATT客户端的连接状态回调* @param gatt:    GATT客户端。* @param status:  连接或断开操作的执行结果, 成功返回 GATT_SUCCESS* @param newState:当前的连接状态:STATE_CONNECTED / STATE_DISCONNECTED*/@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);Log.d(TAG, "onConnectionStateChange() called with: gatt = [" + gatt.toString() + "], " +"status = [" + status + "], newState = [" + newState + "]");if (status == BluetoothGatt.GATT_SUCCESS){Log.d(TAG, "onConnectionStateChange: 连接成功");if (newState == BluetoothGatt.STATE_CONNECTED){/** 异步操作,发现服务完成时,会回调onServicesDiscovered()方法。* 假如发现服务已在启动状态中,则返回true*/boolean serviceStatus = gatt.discoverServices();if (serviceStatus){Log.d(TAG, "onConnectionStateChange: 服务已在启动状态中");}}}else if(newState == BluetoothGatt.STATE_DISCONNECTED){Log.d(TAG, "onConnectionStateChange: 连接断开");}}/*** @param gatt:   执行发现服务后的GATT客户端。* @param status: 发现服务的执行结果, 成功返回 GATT_SUCCESS*/@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);Log.d(TAG, "onServicesDiscovered() called with: gatt = [" + gatt + "], status = [" + status + "]");if (status == BluetoothGatt.GATT_SUCCESS){Log.d(TAG, "onServicesDiscovered: 连接服务成功,可以执行读写操作了");mBluetoothGatt = gatt;/* 通过服务的UUID,获取指定的服务,* 如果远程设备不支持给定UUID的服务,返回null,* 如果远程设备存在多个给定UUID的服务实例,则返回第一个实例 */
//                mBluetoothGatt.getService(UUID);//获取指定UUID对应的服务List<BluetoothGattService> services = mBluetoothGatt.getServices();//获取该设备的服务列表if (services != null){for (BluetoothGattService gattService : services){Log.d(TAG, "onServicesDiscovered: "+gattService.toString());
//                        BluetoothGattCharacteristic bluetoothGattCharacteristic = gattService.getCharacteristic(UUID);//获取指定UUID对应的特征List<BluetoothGattCharacteristic> characteristicList = gattService.getCharacteristics();//获取该设备的特征列表if (characteristicList != null){for (BluetoothGattCharacteristic characteristic : characteristicList){/* 从关联的远程设备读取请求的特征,* 异步操作,请求发起成功则返回true,读取完成会回调:* BluetoothGattCallback.onCharacteristicRead() */boolean readStatus = mBluetoothGatt.readCharacteristic(characteristic);Log.d(TAG, "onServicesDiscovered: 读请求状态 = "+readStatus);/* 将给定的特征及其值写入关联的远程设备,* 异步操作,请求发起成功则返回true,写入完成会回调:* BluetoothGattCallback.onCharacteristicWrite() */boolean writeStatus =  mBluetoothGatt.writeCharacteristic(characteristic);Log.d(TAG, "onServicesDiscovered: 读请求状态 = "+writeStatus);//还有通知类}}}}else {Log.d(TAG, "onServicesDiscovered: 未找到服务");}}}/*** 写操作的回调* @param characteristic:  写入后的特征*                          注意:这里返回的特征,为设备当前的特征,*                          应该在该回调中,应对比该特征的内容是否符合期望值,*                          如果与期望值不同,应该选择重发或终止写入。* @param status:          写入结果,成功为 GATT_SUCCESS*              注意:写入限制20字节,但是api21以上可以尝试改变大小*/@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicWrite(gatt, characteristic, status);Log.d(TAG, "onCharacteristicWrite() called with: gatt = [" + gatt + "]," +" characteristic = [" + characteristic + "], status = [" + status + "]");}/*** 读操作的回调* @param characteristic:  读取后的特征* @param status:          读取结果,成功为 GATT_SUCCESS*/@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicRead(gatt, characteristic, status);Log.d(TAG, "onCharacteristicRead() called with: gatt = [" + gatt + "], " +"characteristic = [" + characteristic + "], status = [" + status + "]");}/*** 读取信息的回调*/@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);Log.d(TAG, "onCharacteristicChanged() called with: gatt = [" + gatt + "], characteristic = [" + characteristic + "]");}};/****  获取已适配的蓝牙记录列表*/public Set<BluetoothDevice> getBondedDevices(){return mBluetoothAdapter.getBondedDevices();}}

 

  相关解决方案