diff --git a/app/src/main/java/com/iflytop/profilometer/api/ble/BleApi.java b/app/src/main/java/com/iflytop/profilometer/api/ble/BleApi.java index 63db3e5..752b65a 100644 --- a/app/src/main/java/com/iflytop/profilometer/api/ble/BleApi.java +++ b/app/src/main/java/com/iflytop/profilometer/api/ble/BleApi.java @@ -1,10 +1,8 @@ package com.iflytop.profilometer.api.ble; import android.content.Context; -import android.os.Build; import android.util.Log; -import com.iflytop.profilometer.ProfilometerApplication; import com.iflytop.profilometer.common.result.Result; import com.iflytop.profilometer.core.bluetooth.BleManager; @@ -47,5 +45,30 @@ public class BleApi { } } + /** + * 链接蓝牙设备 + */ + public String connect(String mac) { + try { + BleManager.getInstance(context).connectToDevice(mac); + return Result.success(); + } catch (Exception e) { + Log.e(TAG, "链接蓝牙设备失败", e); + return Result.failed("链接蓝牙设备失败"); + } + } + + /** + * 断开链接蓝牙设备 + */ + public String disconnect() { + try { + BleManager.getInstance(context).disconnect(); + return Result.success(); + } catch (Exception e) { + Log.e(TAG, "断开蓝牙设备链接失败", e); + return Result.failed("断开蓝牙设备链接失败"); + } + } } diff --git a/app/src/main/java/com/iflytop/profilometer/api/ble/BleRoutes.kt b/app/src/main/java/com/iflytop/profilometer/api/ble/BleRoutes.kt index fb04e47..8900975 100644 --- a/app/src/main/java/com/iflytop/profilometer/api/ble/BleRoutes.kt +++ b/app/src/main/java/com/iflytop/profilometer/api/ble/BleRoutes.kt @@ -31,12 +31,17 @@ fun Routing.bleRoutes(context: Context) { * 链接蓝牙设备 */ post("/api/ble/connect") { + val params = call.receive>() + val jsonResponse = api.connect(params["mac"]) + call.respondText(jsonResponse, ContentType.Application.Json) } /** * 断开链接蓝牙设备 */ post("/api/ble/disconnect") { + val jsonResponse = api.disconnect() + call.respondText(jsonResponse, ContentType.Application.Json) } } diff --git a/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleManager.java b/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleManager.java index 569de73..5a3e96f 100644 --- a/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleManager.java +++ b/app/src/main/java/com/iflytop/profilometer/core/bluetooth/BleManager.java @@ -26,7 +26,7 @@ import java.util.ArrayList; import java.util.List; /** - * BleManager 用于管理蓝牙相关的功能,包括权限检查、扫描设备和连接设备。 + * BleManager 用于管理蓝牙相关的功能,包括权限检查、扫描设备、连接和断开设备。 * 本类采用单例模式,确保整个应用中只有一个实例。 */ public class BleManager { @@ -35,27 +35,21 @@ public class BleManager { // 单例实例 private static BleManager instance; - // Android 系统蓝牙管理器 private final BluetoothManager bluetoothManager; - // 蓝牙适配器 private BluetoothAdapter bluetoothAdapter; - // 蓝牙低功耗扫描器 private BluetoothLeScanner bluetoothLeScanner; - // 保存扫描到的蓝牙设备列表 private final List scannedDevices = new ArrayList<>(); - // 扫描回调对象 private ScanCallback scanCallback; - // 蓝牙连接操作返回的 BluetoothGatt 对象 private BluetoothGatt bluetoothGatt; - // 应用上下文(使用 ApplicationContext 防止内存泄露) private final Context context; /** * 私有构造函数,防止外部直接实例化。 + * * @param context 上下文 */ private BleManager(Context context) { - // 使用 ApplicationContext 避免内存泄露 + // 使用 ApplicationContext 防止内存泄露 this.context = context.getApplicationContext(); bluetoothManager = (BluetoothManager) this.context.getSystemService(Context.BLUETOOTH_SERVICE); if (bluetoothManager != null) { @@ -64,7 +58,8 @@ public class BleManager { } /** - * 获取单例实例,确保整个应用中只有一个 BleManager 对象。 + * 获取单例实例,确保全局只有一个 BleManager 对象。 + * * @param context 上下文 * @return 单例 BleManager 实例 */ @@ -77,7 +72,8 @@ public class BleManager { /** * 辅助方法:检查指定权限是否已被授予。 - * @param permission 权限名称,例如 Manifest.permission.BLUETOOTH_SCAN + * + * @param permission 权限名称 * @return true 表示已授予,false 表示未授予 */ 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 */ @@ -110,7 +105,7 @@ public class BleManager { /** * 提示用户蓝牙未开启。 - * 注意:此方法不自动开启蓝牙,而是直接通过 Toast 提示用户“蓝牙未开启,请先开启蓝牙”。 + * 如果蓝牙未开启,则直接通过 Toast 提示用户“蓝牙未开启,请先开启蓝牙”。 * * @param activity 当前 Activity */ @@ -122,13 +117,15 @@ public class BleManager { if (bluetoothAdapter.isEnabled()) { return; } - // 直接提示用户蓝牙未开启,无需弹出对话框要求开启 + // 提示用户蓝牙未开启 Toast.makeText(activity, "蓝牙未开启,请先开启蓝牙", Toast.LENGTH_LONG).show(); + // 可选:这里不再调用自动开启逻辑,而是等待用户手动开启蓝牙 + // startScan() 内部也会检查蓝牙状态 } /** - * 开始扫描 BLE 设备,扫描结果将保存到 scannedDevices 列表中。 - * 需要确保蓝牙已开启且 BLUETOOTH_SCAN 权限已被授予。 + * 开始扫描 BLE 设备,扫描结果保存到 scannedDevices 列表中。 + * 需要确保蓝牙已开启且 BLUETOOTH_SCAN 权限已授予。 */ @SuppressLint("MissingPermission") public void startScan() { @@ -145,14 +142,12 @@ 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()); @@ -175,13 +170,12 @@ public class BleManager { Log.e(TAG, "BLE 扫描失败,错误码:" + errorCode); } }; - // 开始扫描 bluetoothLeScanner.startScan(scanCallback); } /** * 停止 BLE 设备扫描。 - * 需要确保 BLUETOOTH_SCAN 权限已被授予。 + * 需要确保蓝牙已开启且具有 BLUETOOTH_SCAN 权限。 */ @SuppressLint("MissingPermission") public void stopScan() { @@ -200,19 +194,17 @@ public class BleManager { /** * 返回当前扫描到的设备列表。 - * 直接返回内部保存的 List 对象。 * - * @return 蓝牙设备列表 + * @return List 扫描到的蓝牙设备列表 */ public List getScannedDevices() { return scannedDevices; } /** - * 判断是否已经有设备建立了蓝牙连接。 - * 这里通过 BluetoothGatt 是否为 null 进行简单判断。 + * 判断是否已有设备建立了蓝牙连接(简单判断 BluetoothGatt 是否非空)。 * - * @return 如果已有设备连接返回 true,否则返回 false + * @return true 表示已有设备连接,否则返回 false */ public boolean isConnected() { return bluetoothGatt != null; @@ -220,9 +212,10 @@ public class BleManager { /** * 尝试连接指定的 BLE 设备。 - * 传入设备的 MAC 地址,通过 bluetoothAdapter.getRemoteDevice() 获取 BluetoothDevice 对象,再调用 connectGatt 进行连接。 + * 如果当前已连接的设备和传入的 MAC 地址相同,则不做任何操作; + * 如果当前已连接其他设备,则先断开当前连接,再连接新设备。 * - * @param macAddress 设备的 MAC 地址字符串 + * @param macAddress 目标设备的 MAC 地址字符串 */ @SuppressLint("MissingPermission") public void connectToDevice(String macAddress) { @@ -234,10 +227,22 @@ public class BleManager { Log.e(TAG, "BLUETOOTH_CONNECT 权限未授予"); return; } + // 如果当前已有设备连接 + if (bluetoothGatt != null) { + BluetoothDevice connectedDevice = bluetoothGatt.getDevice(); + if (connectedDevice != null && connectedDevice.getAddress().equals(macAddress)) { + // 如果新连接的设备与当前已连接设备相同,则不做任何操作 + Log.d(TAG, "设备已连接,无需重复连接: " + connectedDevice.getName()); + return; + } else { + // 否则断开当前连接 + disconnect(); + } + } 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) { @@ -253,4 +258,19 @@ public class BleManager { 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, "当前蓝牙设备已断开连接"); + } + } }