|
|
@ -25,21 +25,37 @@ import androidx.core.content.ContextCompat; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.List; |
|
|
|
|
|
|
|
/** |
|
|
|
* BleManager 用于管理蓝牙相关的功能,包括权限检查、扫描设备和连接设备。 |
|
|
|
* 本类采用单例模式,确保整个应用中只有一个实例。 |
|
|
|
*/ |
|
|
|
public class BleManager { |
|
|
|
|
|
|
|
private static final String TAG = "BLEManager"; |
|
|
|
// 单例实例 |
|
|
|
private static BleManager instance; |
|
|
|
|
|
|
|
private BluetoothManager bluetoothManager; |
|
|
|
// Android 系统蓝牙管理器 |
|
|
|
private final BluetoothManager bluetoothManager; |
|
|
|
// 蓝牙适配器 |
|
|
|
private BluetoothAdapter bluetoothAdapter; |
|
|
|
// 蓝牙低功耗扫描器 |
|
|
|
private BluetoothLeScanner bluetoothLeScanner; |
|
|
|
private List<BluetoothDevice> scannedDevices = new ArrayList<>(); |
|
|
|
// 保存扫描到的蓝牙设备列表 |
|
|
|
private final List<BluetoothDevice> scannedDevices = new ArrayList<>(); |
|
|
|
// 扫描回调对象 |
|
|
|
private ScanCallback scanCallback; |
|
|
|
// 蓝牙连接操作返回的 BluetoothGatt 对象 |
|
|
|
private BluetoothGatt bluetoothGatt; |
|
|
|
private Context context; |
|
|
|
// 应用上下文(使用 ApplicationContext 防止内存泄露) |
|
|
|
private final Context context; |
|
|
|
|
|
|
|
/** |
|
|
|
* 私有构造函数,防止外部直接实例化。 |
|
|
|
* @param context 上下文 |
|
|
|
*/ |
|
|
|
private BleManager(Context context) { |
|
|
|
// 使用 ApplicationContext 避免内存泄露 |
|
|
|
this.context = context.getApplicationContext(); |
|
|
|
bluetoothManager = (BluetoothManager) this.context.getSystemService(Context.BLUETOOTH_SERVICE); |
|
|
|
if (bluetoothManager != null) { |
|
|
@ -48,7 +64,9 @@ public class BleManager { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取单例实例 |
|
|
|
* 获取单例实例,确保整个应用中只有一个 BleManager 对象。 |
|
|
|
* @param context 上下文 |
|
|
|
* @return 单例 BleManager 实例 |
|
|
|
*/ |
|
|
|
public static synchronized BleManager getInstance(Context context) { |
|
|
|
if (instance == null) { |
|
|
@ -58,15 +76,20 @@ public class BleManager { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 辅助方法:检查指定权限是否已被授予 |
|
|
|
* 辅助方法:检查指定权限是否已被授予。 |
|
|
|
* @param permission 权限名称,例如 Manifest.permission.BLUETOOTH_SCAN |
|
|
|
* @return true 表示已授予,false 表示未授予 |
|
|
|
*/ |
|
|
|
private boolean hasPermission(String permission) { |
|
|
|
return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 检查并请求必要权限(例如:位置、BLUETOOTH_SCAN、BLUETOOTH_CONNECT)。 |
|
|
|
* 此方法需要在 Activity 中调用,且在 onRequestPermissionsResult 中处理回调。 |
|
|
|
* 检查并请求必要的权限(例如:位置权限、BLUETOOTH_SCAN、BLUETOOTH_CONNECT)。 |
|
|
|
* 该方法需要在 Activity 中调用,并在 onRequestPermissionsResult 中处理回调。 |
|
|
|
* 需要 API 31(Android S)及以上版本。 |
|
|
|
* |
|
|
|
* @param activity 当前 Activity |
|
|
|
*/ |
|
|
|
@RequiresApi(api = Build.VERSION_CODES.S) |
|
|
|
public void checkAndRequestPermissions(Activity activity) { |
|
|
@ -85,7 +108,12 @@ public class BleManager { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 提示用户蓝牙未开启。 |
|
|
|
* 注意:此方法不自动开启蓝牙,而是直接通过 Toast 提示用户“蓝牙未开启,请先开启蓝牙”。 |
|
|
|
* |
|
|
|
* @param activity 当前 Activity |
|
|
|
*/ |
|
|
|
public void promptAndEnableBluetooth(Activity activity) { |
|
|
|
if (bluetoothAdapter == null) { |
|
|
|
Log.e(TAG, "该设备不支持蓝牙"); |
|
|
@ -94,13 +122,13 @@ public class BleManager { |
|
|
|
if (bluetoothAdapter.isEnabled()) { |
|
|
|
return; |
|
|
|
} |
|
|
|
// 直接提示用户蓝牙未开启,无需点击开启 |
|
|
|
// 直接提示用户蓝牙未开启,无需弹出对话框要求开启 |
|
|
|
Toast.makeText(activity, "蓝牙未开启,请先开启蓝牙", Toast.LENGTH_LONG).show(); |
|
|
|
startScan(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 开始扫描 BLE 设备,扫描结果保存到 scannedDevices 列表中 |
|
|
|
* 开始扫描 BLE 设备,扫描结果将保存到 scannedDevices 列表中。 |
|
|
|
* 需要确保蓝牙已开启且 BLUETOOTH_SCAN 权限已被授予。 |
|
|
|
*/ |
|
|
|
@SuppressLint("MissingPermission") |
|
|
|
public void startScan() { |
|
|
@ -117,11 +145,14 @@ public class BleManager { |
|
|
|
Log.e(TAG, "获取 BluetoothLeScanner 失败"); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 清空上一次扫描的结果 |
|
|
|
scannedDevices.clear(); |
|
|
|
// 定义扫描回调 |
|
|
|
scanCallback = new ScanCallback() { |
|
|
|
@Override |
|
|
|
public void onScanResult(int callbackType, ScanResult result) { |
|
|
|
BluetoothDevice device = result.getDevice(); |
|
|
|
// 添加新设备到列表中,避免重复 |
|
|
|
if (device != null && !scannedDevices.contains(device)) { |
|
|
|
scannedDevices.add(device); |
|
|
|
Log.d(TAG, "发现设备: " + device.getName() + " - " + device.getAddress()); |
|
|
@ -144,11 +175,13 @@ public class BleManager { |
|
|
|
Log.e(TAG, "BLE 扫描失败,错误码:" + errorCode); |
|
|
|
} |
|
|
|
}; |
|
|
|
// 开始扫描 |
|
|
|
bluetoothLeScanner.startScan(scanCallback); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 停止 BLE 设备扫描 |
|
|
|
* 停止 BLE 设备扫描。 |
|
|
|
* 需要确保 BLUETOOTH_SCAN 权限已被授予。 |
|
|
|
*/ |
|
|
|
@SuppressLint("MissingPermission") |
|
|
|
public void stopScan() { |
|
|
@ -166,39 +199,58 @@ public class BleManager { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 返回内部扫描到的设备列表,直接返回 List<BluetoothDevice> |
|
|
|
* 返回当前扫描到的设备列表。 |
|
|
|
* 直接返回内部保存的 List<BluetoothDevice> 对象。 |
|
|
|
* |
|
|
|
* @return 蓝牙设备列表 |
|
|
|
*/ |
|
|
|
public List<BluetoothDevice> getScannedDevices() { |
|
|
|
return scannedDevices; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 判断是否已有蓝牙设备连接(简单判断 BluetoothGatt 是否非空) |
|
|
|
* 判断是否已经有设备建立了蓝牙连接。 |
|
|
|
* 这里通过 BluetoothGatt 是否为 null 进行简单判断。 |
|
|
|
* |
|
|
|
* @return 如果已有设备连接返回 true,否则返回 false |
|
|
|
*/ |
|
|
|
public boolean isConnected() { |
|
|
|
return bluetoothGatt != null; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 尝试连接指定的 BLE 设备 |
|
|
|
* 尝试连接指定的 BLE 设备。 |
|
|
|
* 传入设备的 MAC 地址,通过 bluetoothAdapter.getRemoteDevice() 获取 BluetoothDevice 对象,再调用 connectGatt 进行连接。 |
|
|
|
* |
|
|
|
* @param macAddress 设备的 MAC 地址字符串 |
|
|
|
*/ |
|
|
|
@SuppressLint("MissingPermission") |
|
|
|
public void connectToDevice(BluetoothDevice device) { |
|
|
|
if (device == null) return; |
|
|
|
public void connectToDevice(String macAddress) { |
|
|
|
if (macAddress == null || macAddress.isEmpty()) { |
|
|
|
Log.e(TAG, "MAC 地址为空"); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (!hasPermission(Manifest.permission.BLUETOOTH_CONNECT)) { |
|
|
|
Log.e(TAG, "BLUETOOTH_CONNECT 权限未授予"); |
|
|
|
return; |
|
|
|
} |
|
|
|
bluetoothGatt = device.connectGatt(context, false, new BluetoothGattCallback() { |
|
|
|
@Override |
|
|
|
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { |
|
|
|
if (newState == BluetoothProfile.STATE_CONNECTED) { |
|
|
|
Log.d(TAG, "设备已连接: " + device.getName()); |
|
|
|
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) { |
|
|
|
Log.d(TAG, "设备已断开: " + device.getName()); |
|
|
|
bluetoothGatt = null; |
|
|
|
try { |
|
|
|
// 根据 MAC 地址获取 BluetoothDevice 对象 |
|
|
|
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(macAddress); |
|
|
|
// 建立蓝牙连接,connectGatt 为异步操作 |
|
|
|
bluetoothGatt = device.connectGatt(context, false, new BluetoothGattCallback() { |
|
|
|
@Override |
|
|
|
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { |
|
|
|
if (newState == BluetoothProfile.STATE_CONNECTED) { |
|
|
|
Log.d(TAG, "设备已连接: " + device.getName()); |
|
|
|
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) { |
|
|
|
Log.d(TAG, "设备已断开: " + device.getName()); |
|
|
|
bluetoothGatt = null; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} catch (IllegalArgumentException e) { |
|
|
|
Log.e(TAG, "无效的 MAC 地址: " + macAddress, e); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |