|
@ -26,7 +26,7 @@ import java.util.ArrayList; |
|
|
import java.util.List; |
|
|
import java.util.List; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* BleManager 用于管理蓝牙相关的功能,包括权限检查、扫描设备和连接设备。 |
|
|
|
|
|
|
|
|
* BleManager 用于管理蓝牙相关的功能,包括权限检查、扫描设备、连接和断开设备。 |
|
|
* 本类采用单例模式,确保整个应用中只有一个实例。 |
|
|
* 本类采用单例模式,确保整个应用中只有一个实例。 |
|
|
*/ |
|
|
*/ |
|
|
public class BleManager { |
|
|
public class BleManager { |
|
@ -35,27 +35,21 @@ public class BleManager { |
|
|
// 单例实例 |
|
|
// 单例实例 |
|
|
private static BleManager instance; |
|
|
private static BleManager instance; |
|
|
|
|
|
|
|
|
// Android 系统蓝牙管理器 |
|
|
|
|
|
private final BluetoothManager bluetoothManager; |
|
|
private final BluetoothManager bluetoothManager; |
|
|
// 蓝牙适配器 |
|
|
|
|
|
private BluetoothAdapter bluetoothAdapter; |
|
|
private BluetoothAdapter bluetoothAdapter; |
|
|
// 蓝牙低功耗扫描器 |
|
|
|
|
|
private BluetoothLeScanner bluetoothLeScanner; |
|
|
private BluetoothLeScanner bluetoothLeScanner; |
|
|
// 保存扫描到的蓝牙设备列表 |
|
|
|
|
|
private final List<BluetoothDevice> scannedDevices = new ArrayList<>(); |
|
|
private final List<BluetoothDevice> scannedDevices = new ArrayList<>(); |
|
|
// 扫描回调对象 |
|
|
|
|
|
private ScanCallback scanCallback; |
|
|
private ScanCallback scanCallback; |
|
|
// 蓝牙连接操作返回的 BluetoothGatt 对象 |
|
|
|
|
|
private BluetoothGatt bluetoothGatt; |
|
|
private BluetoothGatt bluetoothGatt; |
|
|
// 应用上下文(使用 ApplicationContext 防止内存泄露) |
|
|
|
|
|
private final Context context; |
|
|
private final Context context; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 私有构造函数,防止外部直接实例化。 |
|
|
* 私有构造函数,防止外部直接实例化。 |
|
|
|
|
|
* |
|
|
* @param context 上下文 |
|
|
* @param context 上下文 |
|
|
*/ |
|
|
*/ |
|
|
private BleManager(Context context) { |
|
|
private BleManager(Context context) { |
|
|
// 使用 ApplicationContext 避免内存泄露 |
|
|
|
|
|
|
|
|
// 使用 ApplicationContext 防止内存泄露 |
|
|
this.context = context.getApplicationContext(); |
|
|
this.context = context.getApplicationContext(); |
|
|
bluetoothManager = (BluetoothManager) this.context.getSystemService(Context.BLUETOOTH_SERVICE); |
|
|
bluetoothManager = (BluetoothManager) this.context.getSystemService(Context.BLUETOOTH_SERVICE); |
|
|
if (bluetoothManager != null) { |
|
|
if (bluetoothManager != null) { |
|
@ -64,7 +58,8 @@ public class BleManager { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 获取单例实例,确保整个应用中只有一个 BleManager 对象。 |
|
|
|
|
|
|
|
|
* 获取单例实例,确保全局只有一个 BleManager 对象。 |
|
|
|
|
|
* |
|
|
* @param context 上下文 |
|
|
* @param context 上下文 |
|
|
* @return 单例 BleManager 实例 |
|
|
* @return 单例 BleManager 实例 |
|
|
*/ |
|
|
*/ |
|
@ -77,7 +72,8 @@ public class BleManager { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 辅助方法:检查指定权限是否已被授予。 |
|
|
* 辅助方法:检查指定权限是否已被授予。 |
|
|
* @param permission 权限名称,例如 Manifest.permission.BLUETOOTH_SCAN |
|
|
|
|
|
|
|
|
* |
|
|
|
|
|
* @param permission 权限名称 |
|
|
* @return true 表示已授予,false 表示未授予 |
|
|
* @return true 表示已授予,false 表示未授予 |
|
|
*/ |
|
|
*/ |
|
|
private boolean hasPermission(String permission) { |
|
|
private boolean hasPermission(String permission) { |
|
@ -85,9 +81,8 @@ public class BleManager { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 检查并请求必要的权限(例如:位置权限、BLUETOOTH_SCAN、BLUETOOTH_CONNECT)。 |
|
|
|
|
|
* 该方法需要在 Activity 中调用,并在 onRequestPermissionsResult 中处理回调。 |
|
|
|
|
|
* 需要 API 31(Android S)及以上版本。 |
|
|
|
|
|
|
|
|
* 检查并请求必要权限(例如:位置、BLUETOOTH_SCAN、BLUETOOTH_CONNECT)。 |
|
|
|
|
|
* 此方法需要在 Activity 中调用,并在 onRequestPermissionsResult 中处理回调。 |
|
|
* |
|
|
* |
|
|
* @param activity 当前 Activity |
|
|
* @param activity 当前 Activity |
|
|
*/ |
|
|
*/ |
|
@ -110,7 +105,7 @@ public class BleManager { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 提示用户蓝牙未开启。 |
|
|
* 提示用户蓝牙未开启。 |
|
|
* 注意:此方法不自动开启蓝牙,而是直接通过 Toast 提示用户“蓝牙未开启,请先开启蓝牙”。 |
|
|
|
|
|
|
|
|
* 如果蓝牙未开启,则直接通过 Toast 提示用户“蓝牙未开启,请先开启蓝牙”。 |
|
|
* |
|
|
* |
|
|
* @param activity 当前 Activity |
|
|
* @param activity 当前 Activity |
|
|
*/ |
|
|
*/ |
|
@ -122,13 +117,15 @@ public class BleManager { |
|
|
if (bluetoothAdapter.isEnabled()) { |
|
|
if (bluetoothAdapter.isEnabled()) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
// 直接提示用户蓝牙未开启,无需弹出对话框要求开启 |
|
|
|
|
|
|
|
|
// 提示用户蓝牙未开启 |
|
|
Toast.makeText(activity, "蓝牙未开启,请先开启蓝牙", Toast.LENGTH_LONG).show(); |
|
|
Toast.makeText(activity, "蓝牙未开启,请先开启蓝牙", Toast.LENGTH_LONG).show(); |
|
|
|
|
|
// 可选:这里不再调用自动开启逻辑,而是等待用户手动开启蓝牙 |
|
|
|
|
|
// startScan() 内部也会检查蓝牙状态 |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 开始扫描 BLE 设备,扫描结果将保存到 scannedDevices 列表中。 |
|
|
|
|
|
* 需要确保蓝牙已开启且 BLUETOOTH_SCAN 权限已被授予。 |
|
|
|
|
|
|
|
|
* 开始扫描 BLE 设备,扫描结果保存到 scannedDevices 列表中。 |
|
|
|
|
|
* 需要确保蓝牙已开启且 BLUETOOTH_SCAN 权限已授予。 |
|
|
*/ |
|
|
*/ |
|
|
@SuppressLint("MissingPermission") |
|
|
@SuppressLint("MissingPermission") |
|
|
public void startScan() { |
|
|
public void startScan() { |
|
@ -145,14 +142,12 @@ public class BleManager { |
|
|
Log.e(TAG, "获取 BluetoothLeScanner 失败"); |
|
|
Log.e(TAG, "获取 BluetoothLeScanner 失败"); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
// 清空上一次扫描的结果 |
|
|
|
|
|
|
|
|
// 清空上一次扫描结果 |
|
|
scannedDevices.clear(); |
|
|
scannedDevices.clear(); |
|
|
// 定义扫描回调 |
|
|
|
|
|
scanCallback = new ScanCallback() { |
|
|
scanCallback = new ScanCallback() { |
|
|
@Override |
|
|
@Override |
|
|
public void onScanResult(int callbackType, ScanResult result) { |
|
|
public void onScanResult(int callbackType, ScanResult result) { |
|
|
BluetoothDevice device = result.getDevice(); |
|
|
BluetoothDevice device = result.getDevice(); |
|
|
// 添加新设备到列表中,避免重复 |
|
|
|
|
|
if (device != null && !scannedDevices.contains(device)) { |
|
|
if (device != null && !scannedDevices.contains(device)) { |
|
|
scannedDevices.add(device); |
|
|
scannedDevices.add(device); |
|
|
Log.d(TAG, "发现设备: " + device.getName() + " - " + device.getAddress()); |
|
|
Log.d(TAG, "发现设备: " + device.getName() + " - " + device.getAddress()); |
|
@ -175,13 +170,12 @@ public class BleManager { |
|
|
Log.e(TAG, "BLE 扫描失败,错误码:" + errorCode); |
|
|
Log.e(TAG, "BLE 扫描失败,错误码:" + errorCode); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
// 开始扫描 |
|
|
|
|
|
bluetoothLeScanner.startScan(scanCallback); |
|
|
bluetoothLeScanner.startScan(scanCallback); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 停止 BLE 设备扫描。 |
|
|
* 停止 BLE 设备扫描。 |
|
|
* 需要确保 BLUETOOTH_SCAN 权限已被授予。 |
|
|
|
|
|
|
|
|
* 需要确保蓝牙已开启且具有 BLUETOOTH_SCAN 权限。 |
|
|
*/ |
|
|
*/ |
|
|
@SuppressLint("MissingPermission") |
|
|
@SuppressLint("MissingPermission") |
|
|
public void stopScan() { |
|
|
public void stopScan() { |
|
@ -200,19 +194,17 @@ public class BleManager { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 返回当前扫描到的设备列表。 |
|
|
* 返回当前扫描到的设备列表。 |
|
|
* 直接返回内部保存的 List<BluetoothDevice> 对象。 |
|
|
|
|
|
* |
|
|
* |
|
|
* @return 蓝牙设备列表 |
|
|
|
|
|
|
|
|
* @return List<BluetoothDevice> 扫描到的蓝牙设备列表 |
|
|
*/ |
|
|
*/ |
|
|
public List<BluetoothDevice> getScannedDevices() { |
|
|
public List<BluetoothDevice> getScannedDevices() { |
|
|
return scannedDevices; |
|
|
return scannedDevices; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 判断是否已经有设备建立了蓝牙连接。 |
|
|
|
|
|
* 这里通过 BluetoothGatt 是否为 null 进行简单判断。 |
|
|
|
|
|
|
|
|
* 判断是否已有设备建立了蓝牙连接(简单判断 BluetoothGatt 是否非空)。 |
|
|
* |
|
|
* |
|
|
* @return 如果已有设备连接返回 true,否则返回 false |
|
|
|
|
|
|
|
|
* @return true 表示已有设备连接,否则返回 false |
|
|
*/ |
|
|
*/ |
|
|
public boolean isConnected() { |
|
|
public boolean isConnected() { |
|
|
return bluetoothGatt != null; |
|
|
return bluetoothGatt != null; |
|
@ -220,9 +212,10 @@ public class BleManager { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 尝试连接指定的 BLE 设备。 |
|
|
* 尝试连接指定的 BLE 设备。 |
|
|
* 传入设备的 MAC 地址,通过 bluetoothAdapter.getRemoteDevice() 获取 BluetoothDevice 对象,再调用 connectGatt 进行连接。 |
|
|
|
|
|
|
|
|
* 如果当前已连接的设备和传入的 MAC 地址相同,则不做任何操作; |
|
|
|
|
|
* 如果当前已连接其他设备,则先断开当前连接,再连接新设备。 |
|
|
* |
|
|
* |
|
|
* @param macAddress 设备的 MAC 地址字符串 |
|
|
|
|
|
|
|
|
* @param macAddress 目标设备的 MAC 地址字符串 |
|
|
*/ |
|
|
*/ |
|
|
@SuppressLint("MissingPermission") |
|
|
@SuppressLint("MissingPermission") |
|
|
public void connectToDevice(String macAddress) { |
|
|
public void connectToDevice(String macAddress) { |
|
@ -234,10 +227,22 @@ public class BleManager { |
|
|
Log.e(TAG, "BLUETOOTH_CONNECT 权限未授予"); |
|
|
Log.e(TAG, "BLUETOOTH_CONNECT 权限未授予"); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
// 如果当前已有设备连接 |
|
|
|
|
|
if (bluetoothGatt != null) { |
|
|
|
|
|
BluetoothDevice connectedDevice = bluetoothGatt.getDevice(); |
|
|
|
|
|
if (connectedDevice != null && connectedDevice.getAddress().equals(macAddress)) { |
|
|
|
|
|
// 如果新连接的设备与当前已连接设备相同,则不做任何操作 |
|
|
|
|
|
Log.d(TAG, "设备已连接,无需重复连接: " + connectedDevice.getName()); |
|
|
|
|
|
return; |
|
|
|
|
|
} else { |
|
|
|
|
|
// 否则断开当前连接 |
|
|
|
|
|
disconnect(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
try { |
|
|
try { |
|
|
// 根据 MAC 地址获取 BluetoothDevice 对象 |
|
|
// 根据 MAC 地址获取 BluetoothDevice 对象 |
|
|
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(macAddress); |
|
|
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(macAddress); |
|
|
// 建立蓝牙连接,connectGatt 为异步操作 |
|
|
|
|
|
|
|
|
// 建立蓝牙连接(异步操作) |
|
|
bluetoothGatt = device.connectGatt(context, false, new BluetoothGattCallback() { |
|
|
bluetoothGatt = device.connectGatt(context, false, new BluetoothGattCallback() { |
|
|
@Override |
|
|
@Override |
|
|
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { |
|
|
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { |
|
@ -253,4 +258,19 @@ public class BleManager { |
|
|
Log.e(TAG, "无效的 MAC 地址: " + macAddress, e); |
|
|
Log.e(TAG, "无效的 MAC 地址: " + macAddress, e); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 断开当前已连接的蓝牙设备。 |
|
|
|
|
|
* 如果当前有连接的设备,则调用 disconnect() 和 close() 方法断开连接, |
|
|
|
|
|
* 并将内部的 bluetoothGatt 置为 null。 |
|
|
|
|
|
*/ |
|
|
|
|
|
@SuppressLint("MissingPermission") |
|
|
|
|
|
public void disconnect() { |
|
|
|
|
|
if (bluetoothGatt != null) { |
|
|
|
|
|
bluetoothGatt.disconnect(); |
|
|
|
|
|
bluetoothGatt.close(); |
|
|
|
|
|
bluetoothGatt = null; |
|
|
|
|
|
Log.d(TAG, "当前蓝牙设备已断开连接"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |