页面切换后“蓝牙连接/数据丢失”的根因与遇到的其他问题的解决办法

一、问题背景

在 Flutter App 中集成 BLE 设备(如血氧仪)后,常见问题:

  • 页面切换(Get.to / Get.back)
  • 实际蓝牙 物理连接仍存在
  • 但 UI 显示:
    • 未连接
    • 数据归零
    • 波形停止
  • 需要重新连接才能恢复

二、根因总结(核心)

BLE 状态被绑定到了页面 / Controller 生命周期

当页面 pop 或 Controller dispose:

  • characteristic 被释放
  • notify 监听被 cancel
  • Rx 状态被重置

👉 导致 “逻辑断连”,而不是“物理断连”


三、核心设计原则(务必牢记)

  • BLE = 全局资源
  • 页面 / Controller = 短生命周期
  • 任何 BLE 状态,都不能放在页面层

四、最终解决方案

架构分层


BLE Service(单例 / 常驻)
├─ connect / disconnect
├─ characteristic / notify
├─ 数据解析
├─ StreamController.broadcast
├─ 连接状态快照
└─ 最新数据快照

Controller(页面级)
├─ 只维护 UI Rx 状态
├─ 订阅 Service Stream
└─ 从 Service 恢复快照

UI Page
└─ 纯展示(Obx)


五、关键实现要点

1️⃣ BLE Service 必须是单例

class BleService {
  static final BleService _instance = BleService._internal();
  factory BleService() => _instance;
  BleService._internal();
}

2️⃣ 所有 Stream 必须 broadcast

final StreamController<T> _ctrl =
    StreamController<T>.broadcast();

原因:

  • 多页面
  • 多 controller
  • 页面销毁不影响数据源

3️⃣ 连接状态 & 快照放在 Service

bool _connected;
String _deviceId;
String _deviceName;

Stream<bool> get connectionStream;
Pc60fOxiData? latestOxi;

4️⃣ Controller onInit 无感恢复


void onInit() {
  connected.value = ble.isConnected;
  deviceName.value = ble.connectedDeviceName;

  final latest = ble.latestOxi;
  if (latest != null) {
    _onData(latest);
  }

  _connSub = ble.connectionStream.listen((v) {
    connected.value = v;
  });
}

5️⃣ 页面切换不影响 BLE

  • Controller 可销毁
  • Service 常驻
  • 页面回来 → 立刻恢复 UI

六、波形“贴底直线”的真实原因

  • 协议波形值:0 ~ 127
  • Controller 误归一化为:0 ~ 1
  • UI 按 0~127 绘制

👉 数值尺度不匹配

正确做法

waveformPoints.add(p.value.toDouble());

七、自动历史记录的节流策略

目标

  • 不高频写 Hive
  • 关键变化不丢

条件

  • 时间 ≥ 5 秒
  • 或 SpO₂ ≥ ±2%
  • 或 PR ≥ ±5 bpm

八、历史记录职责划分

方法职责
_appendHistory_o自动记录(后台、节流)
saveCurrentToHistory用户手动保存
_saveRecord统一写 Hive

九、总结

页面切换后数据丢失,不是 BLE 断了,
而是 BLE 状态放错了层级。

正确做法:
BLE 常驻,Controller 可销毁,UI 只展示。


十、BLE 项目模板骨架

📁 目录结构推荐


lib/
└─ ble/
├─ ble_service.dart        // 单例,BLE 核心
├─ ble_models.dart         // 数据模型
├─ ble_parser.dart         // 协议解析
└─ ble_constants.dart

└─ feature_x/
├─ controller.dart         // UI Controller
├─ page_home.dart
├─ page_connect.dart
└─ page_history.dart


1️⃣ BLE Service 模板(核心)

class BleService {
  static final BleService _i = BleService._internal();
  factory BleService() => _i;
  BleService._internal();

  bool _connected = false;
  String _deviceId = '';
  String _deviceName = '';

  final StreamController<bool> _connCtrl =
      StreamController<bool>.broadcast();

  bool get isConnected => _connected;
  Stream<bool> get connectionStream => _connCtrl.stream;

  Future<void> connect(...) async {
    // connect + discover + notify
    _connected = true;
    _connCtrl.add(true);
  }

  Future<void> disconnect() async {
    _connected = false;
    _connCtrl.add(false);
  }
}

2️⃣ Controller 模板(UI 层)

class FeatureController extends GetxController {
  final ble = BleService();

  final connected = false.obs;
  StreamSubscription<bool>? _connSub;

  
  void onInit() {
    connected.value = ble.isConnected;

    _connSub = ble.connectionStream.listen((v) {
      connected.value = v;
    });
  }

  
  void onClose() {
    _connSub?.cancel();
    super.onClose();
  }
}

3️⃣ UI Page 模板


Widget build(BuildContext context) {
  final c = Get.find<FeatureController>();

  return Obx(() {
    if (!c.connected.value) {
      return const Text('未连接');
    }
    return const Text('已连接');
  });
}

4️⃣ 永久避免问题的 Checklist

  • BLE Service 是否单例?
  • notify 是否只在 Service?
  • Stream 是否 broadcast?
  • Controller 是否只做 UI?
  • 是否有快照(latestData)?
  • 页面切换是否不影响 BLE?

✅ 这个模版解决的问题:

  • 页面随便切,BLE 不掉
  • Controller 随便销毁,状态不丢
  • UI 秒恢复
  • 波形 / 数据 / 历史稳定

不同品牌手机在遇到蓝牙连接后浏览器画面卡死问题时,解决方法既有共性也可能存在差异。 共性方面,无论何种品牌手机,当出现该问题时,一些通用的解决思路是可行的。比如关闭蓝牙,排除蓝牙设备浏览器之间可能存在的冲突;刷新页面或重启浏览器App,清除浏览器缓存和临时数据,恢复正常的页面加载和显示;切换网络,确保网络连接稳定;清理手机内存,关闭其他不必要的后台应用程序,释放内存;更新浏览器和系统,新版本通常会修复一些已知的兼容性和性能问题。 然而,不同品牌手机也可能存在差异。这主要体现在系统设置和优化上。例如,某些品牌手机可能有自己独特的系统管理工具或设置选项,可用于更精准地管理蓝牙连接和应用程序。像华为手机有智能隐私保护和多屏协同等特色功能,可能会影响蓝牙浏览器的交互,在解决问题时可能需要对这些特色功能进行检查和调整;小米手机的MIUI系统有较为丰富的电量管理和后台应用管理策略,可能需要据其特定的设置来优化蓝牙和浏览器的运行环境。 以下是一个简单的伪代码示例,模拟不同品牌手机解决该问题的不同处理方式: ```python def solve_browser_freeze(phone_brand): if phone_brand == "Huawei": # 华为手机的特色处理 check_huawei_special_features() elif phone_brand == "Xiaomi": # 小米手机的特色处理 adjust_xiaomi_settings() else: # 通用处理 common_solve() def check_huawei_special_features(): print("检查华为手机的特色功能设置") def adjust_xiaomi_settings(): print("调整小米手机的特定设置") def common_solve(): print("执行通用解决方法,如关闭蓝牙、刷新页面等") # 模拟不同品牌手机调用解决函数 solve_browser_freeze("Huawei") solve_browser_freeze("Xiaomi") solve_browser_freeze("Other") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值