From e7720308bc4e832e7900953e3b87cb2a052619ec Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Thu, 25 Sep 2025 19:18:32 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=89=8B=E6=9C=BA=E8=93=9D=E7=89=99=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E5=90=8E=EF=BC=8C=E9=87=8D=E6=96=B0=E5=BC=80=E5=90=AF?= =?UTF-8?q?=EF=BC=8C=E5=94=A4=E9=86=92=E4=B8=8D=E4=BA=86=E3=80=82=202.?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E7=B3=BB=E7=BB=9F=E9=87=8D=E5=90=AF=E5=90=8E?= =?UTF-8?q?=EF=BC=8C=E6=B2=A1=E6=9C=89=E9=87=8D=E5=90=AF=EF=BC=88=E6=9D=83?= =?UTF-8?q?=E9=99=90=E6=B2=A1=E6=9C=89=E4=BA=86=EF=BC=89=E3=80=82=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E6=9D=83=E9=99=90=E5=90=8E=EF=BC=8C=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E8=B5=B7=E6=9D=A5=E4=BA=86=EF=BC=8C=E4=BD=86=E6=98=AF=E4=B8=8D?= =?UTF-8?q?=E8=BF=9B=E5=85=A5=E6=89=AB=E6=8F=8F=E8=BF=9E=E6=8E=A5=E3=80=82?= =?UTF-8?q?=203.=E5=90=8E=E5=8F=B0=E6=9C=8D=E5=8A=A1=E5=9C=A8=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E8=80=85=E6=A8=A1=E5=BC=8F=E4=B8=8B=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E8=A2=AB=E6=89=8B=E6=9C=BA=E5=85=B3=E9=97=AD=E3=80=82=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E5=90=8E=EF=BC=8C=E6=9C=8D=E5=8A=A1=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E4=BC=9A=E6=8A=A5=E9=94=99=E3=80=82=204.=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1ble=E6=89=AB=E6=8F=8F=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E5=81=9A=E8=BF=9E=E6=8E=A5=E5=88=A4=E6=96=AD=E3=80=82=E4=B8=80?= =?UTF-8?q?=E7=9B=B4=E5=9C=A8=E5=90=8E=E5=8F=B0=E6=89=AB=E6=8F=8F=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E9=AB=98=E8=80=97=E7=94=B5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent_service/BleMediaService.kt | 15 +++- .../ble_service/BleCompanionManager.kt | 89 +++++++++++++++---- .../yunqiinnovation/ble_service/BleService.kt | 15 +++- .../ble_service/BleServicePlugin.kt | 2 +- .../ble_service/BleWakeReceiver.kt | 55 ++++++++++-- 5 files changed, 143 insertions(+), 33 deletions(-) diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt index 3caa6efa4..9898312af 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt @@ -21,6 +21,7 @@ import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.Dispatchers import com.yunqiinnovation.ble_service.BleConst import com.yunqiinnovation.ble_service.BleService +import com.yunqiinnovation.ble_service.BleCompanionManager // 添加蓝牙相关导入 import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice @@ -95,7 +96,11 @@ class BleMediaService : Service(), CoroutineScope { private var isBluetoothReceiverRegistered = false // 添加定期检查蓝牙状态的协程作业 private var bluetoothCheckJob: kotlinx.coroutines.Job? = null - +/** + * BLE伴侣设备管理器 + * 用于管理设备配对和后台扫描 + */ +private var companionManager: BleCompanionManager? = null /** * 蓝牙设备连接状态广播接收器 * 监听蓝牙设备的连接和断开事件 @@ -169,6 +174,7 @@ class BleMediaService : Service(), CoroutineScope { when (state) { BluetoothAdapter.STATE_OFF -> { Log.w(TAG, "蓝牙已关闭") + //companionManager?.unregisterBackgroundScan() // 蓝牙关闭时,注销设备连接监听器(保留状态监听器) unregisterDeviceConnectionReceiver() } @@ -254,6 +260,9 @@ class BleMediaService : Service(), CoroutineScope { if (deviceName.equals(TARGET_DEVICE_NAME, ignoreCase = true)) { Log.i(TAG, "发现已绑定的目标设备: $deviceName") + val pairedUuid = companionManager?.getPairedUuid() + val pairedMac = companionManager?.getPairedMacAddress() + companionManager?.registerBackgroundScan(pairedMac, pairedUuid) // 注意:这里只能检查绑定状态,无法直接判断连接状态 // 实际连接状态需要通过广播接收器监听 } @@ -390,13 +399,15 @@ class BleMediaService : Service(), CoroutineScope { // 设置服务状态 isRunning.set(true) startForeground(NOTIFICATION_ID, createNotification()) - + // 初始化BLE伴侣设备管理器 - 使用单例模式获取实例 + companionManager = BleCompanionManager.getInstance(this) // 初始化蓝牙监听器 initializeBluetoothListener() // 启动定期蓝牙状态检查 startBluetoothStatusCheck() + Log.i(TAG, "BLE代理服务已启动") } diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt index 83756adaf..e02fe6f52 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt @@ -22,10 +22,12 @@ import java.util.UUID import java.util.regex.Pattern /** - * BleCompanionManager + * BleCompanionManager - 单例模式 * 用于管理BLE设备的配对和关联 + * + * 使用单例模式确保全局只有一个实例,避免重复初始化和资源浪费 */ -class BleCompanionManager(private val context: Context) { +class BleCompanionManager private constructor(private val context: Context) { private val TAG = "BleCompanionManager" // 常量定义 @@ -35,6 +37,43 @@ class BleCompanionManager(private val context: Context) { private const val KEY_ASSOCIATION_IDS = "association_ids" private const val KEY_PAIRED_MAC = "paired_mac" private const val KEY_PAIRED_UUID = "paired_uuid" + + // 单例实例 + @Volatile + private var INSTANCE: BleCompanionManager? = null + + /** + * 获取BleCompanionManager单例实例 + * 使用双重检查锁定模式确保线程安全 + * + * @param context 应用上下文,建议使用ApplicationContext + * @return BleCompanionManager实例 + */ + fun getInstance(context: Context): BleCompanionManager { + return INSTANCE ?: synchronized(this) { + INSTANCE ?: BleCompanionManager(context.applicationContext).also { INSTANCE = it } + } + } + + /** + * 获取已初始化的实例(如果存在) + * + * @return BleCompanionManager实例,如果未初始化则返回null + */ + fun getInstanceIfInitialized(): BleCompanionManager? { + return INSTANCE + } + + /** + * 销毁单例实例 + * 主要用于测试或特殊情况下的清理 + */ + fun destroyInstance() { + synchronized(this) { + INSTANCE?.cleanup() + INSTANCE = null + } + } } // 会话保存 @@ -44,21 +83,37 @@ class BleCompanionManager(private val context: Context) { // 用于验证的临时MAC地址 private var pendingMacAddress: String? = null - // 创建PendingIntent - // 创建PendingIntent - val scanIntent = Intent(BleConst.ACTION_SCAN_RESULT) - .setClass(context, BleWakeReceiver::class.java) - - val pendingIntent = PendingIntent.getBroadcast( - context, - 0, - scanIntent, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_UPDATE_CURRENT - } - ) + + // 创建PendingIntent + private val pendingIntent by lazy { + val scanIntent = Intent(BleConst.ACTION_SCAN_RESULT) + .setClass(context, BleWakeReceiver::class.java) + + PendingIntent.getBroadcast( + context, + 0, + scanIntent, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } + ) + } + + /** + * 清理资源 + * 在销毁实例时调用 + */ + private fun cleanup() { + try { + unregisterBackgroundScan() + pendingMacAddress = null + } catch (e: Exception) { + Log.e(TAG, "清理资源时发生错误: ${e.message}", e) + } + } + /** * 获取CompanionDeviceManager服务(使用Activity上下文) */ diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt index c2a8d578a..cf8cbb834 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt @@ -138,7 +138,7 @@ object BleService { /** 指令发送器实例 */ private val commandSender = BleCommandSender() - + private var companionManager: BleCompanionManager? = null /** * 检查是否有蓝牙扫描权限 * Android 12+ 需要BLUETOOTH_SCAN权限,之前版本需要ACCESS_FINE_LOCATION权限 @@ -597,16 +597,18 @@ object BleService { // ====================================================================================================== /** * 自动连接已配对的BLE设备 + * + * 该方法会尝试连接之前通过CompanionDeviceManager配对的设备 * 如果没有找到已配对的MAC地址,将不会进行连接 */ fun connect() { try { Log.i(TAG, "设备连接与通信: 自动连接已配对的BLE设备") - // 创建 BleCompanionManager 实例 - val companionManager = BleCompanionManager(context) + // 获取 BleCompanionManager 单例实例 + companionManager = BleCompanionManager.getInstance(context) // 获取已配对的 MAC 地址 - val pairedMac = companionManager.getPairedMacAddress() + val pairedMac = companionManager?.getPairedMacAddress() if (pairedMac.isNullOrEmpty()) { Log.w(TAG, "未找到已配对的设备 MAC 地址") @@ -728,6 +730,7 @@ object BleService { status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED -> { updateConnectionState(BleConst.STATE_CONNECTED) Log.d(TAG, "成功连接Ble蓝牙设备:${g.device.name}") + companionManager?.unregisterBackgroundScan() // 设置PHY值为2M以提高传输速度 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { try { @@ -761,7 +764,11 @@ object BleService { newState == BluetoothProfile.STATE_DISCONNECTED -> { updateConnectionState(BleConst.STATE_DISCONNECTED) Log.d(TAG, "监听到已断开连接") + disconnectGatt() + val pairedUuid = companionManager?.getPairedUuid() + val pairedMac = companionManager?.getPairedMacAddress() + companionManager?.registerBackgroundScan(pairedMac, pairedUuid) } else -> { diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt index c718f87eb..a9aac4929 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt @@ -68,7 +68,7 @@ class BleServicePlugin : FlutterPlugin, MethodCallHandler, ActivityAware, BleService.addCallback(this) // 初始化CompanionManager - bleCompanionManager = BleCompanionManager(context) + bleCompanionManager = BleCompanionManager.getInstance(context) } // 设置事件通道 - 简化版 diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleWakeReceiver.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleWakeReceiver.kt index 9f1ba5757..432f69b9c 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleWakeReceiver.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleWakeReceiver.kt @@ -37,9 +37,13 @@ class BleWakeReceiver : BroadcastReceiver() { } } + /** + * 启动BLE服务的改进方法 + * 处理Android系统对后台服务启动的限制 + */ private fun startBleService(context: Context) { try { - // 检查是否存在BleMediaService类,如果不存在则使用BleService + // 检查是否存在BleMediaService类 val serviceClass = try { Log.d(TAG, "startBleService存在") Class.forName("com.yunqiinnovation.agent_service.BleMediaService") @@ -48,22 +52,55 @@ class BleWakeReceiver : BroadcastReceiver() { return } - // 创建服务Intent,并携带MAC地址 + // 创建服务Intent val serviceIntent = Intent().apply { setClassName(context, serviceClass.name) action = BleConst.ACTION_SCAN_RESULT } - - // 启动前台服务 + + // 启动服务的改进策略 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context.startForegroundService(serviceIntent) - Log.i(TAG, "已启动前台服务: ${serviceClass.name}") + try { + // 首先尝试启动前台服务 + Log.i(TAG, "尝试启动前台服务: ${serviceClass.name}") + context.startForegroundService(serviceIntent) + Log.i(TAG, "前台服务启动成功") + } catch (e: IllegalStateException) { + Log.w(TAG, "前台服务启动失败,尝试其他方式: ${e.message}") + + // 如果前台服务启动失败,尝试通过Activity启动 + try { + Log.i(TAG, "通过Activity启动服务成功: ${serviceClass.name}") + val activityIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + putExtra("auto_start_ble_service", true) + putExtra("ble_service_action", BleConst.ACTION_SCAN_RESULT) + } + + if (activityIntent != null) { + context.startActivity(activityIntent) + + Log.i(TAG, "通过Activity启动应用,将在前台启动服务") + } else { + Log.e(TAG, "无法获取应用启动Intent") + } + } catch (activityException: Exception) { + Log.e(TAG, "通过Activity启动也失败: ${activityException.message}") + } + } catch (e: Exception) { + Log.e(TAG, "启动前台服务时发生未知错误: ${e.message}", e) + } } else { - context.startService(serviceIntent) - Log.i(TAG, "已启动服务: ${serviceClass.name}") + // Android 8.0以下直接启动服务 + try { + context.startService(serviceIntent) + Log.i(TAG, "已启动服务: ${serviceClass.name}") + } catch (e: Exception) { + Log.e(TAG, "启动服务失败: ${e.message}", e) + } } } catch (e: Exception) { Log.e(TAG, "启动BLE服务失败: ${e.message}", e) } } -} \ No newline at end of file +} \ No newline at end of file