260 lines
8.9 KiB
Markdown
260 lines
8.9 KiB
Markdown
# 🔥 卡死/闪退问题分析与修复记录
|
||
|
||
> 文档创建: 2026-04-09
|
||
> 最后更新: 2026-04-09
|
||
> 维护者: 前端工程师
|
||
> 说明: 记录项目中所有可能导致应用卡死、闪退的潜在问题,跟踪修复进度
|
||
|
||
---
|
||
|
||
## 📊 问题总览
|
||
|
||
| 等级 | 数量 | 已修复 | 待修复 |
|
||
|------|------|--------|--------|
|
||
| 🔴 高危 | 4 | 4 | 0 |
|
||
| 🟠 中危 | 3 | 3 | 0 |
|
||
| 🟡 低危 | 4 | 3 | 1 |
|
||
| **合计** | **11** | **10** | **1** |
|
||
|
||
---
|
||
|
||
## 🔴 高危问题(直接导致闪退)
|
||
|
||
### #1 Hive `late` Box 未初始化访问
|
||
|
||
- **状态**: ✅ 已修复
|
||
- **优先级**: P0
|
||
- **文件**: `lib/src/services/data/hive_service.dart`
|
||
- **触发路径**:
|
||
1. `MealRecordController.onInit()` → `_loadDayRecords()` → `HiveService().getMealRecordsByDate()`
|
||
2. `getMealRecordsByDate()` 访问 `mealRecords.values`,但 `mealRecords` 是 `late Box`
|
||
3. 如果 `HiveService.init()` 尚未完成,`late` 字段未赋值 → `LateInitializationError` → 闪退
|
||
- **修复方案**:
|
||
- `late Box` 改为 `Box?` 可空类型
|
||
- 所有 getter 加 `_assertInitialized()` 检查
|
||
- Controller 中所有 Hive 读取操作前加 `if (!hive.isInitialized) return;`
|
||
- **修复日期**: 2026-04-09
|
||
|
||
---
|
||
|
||
### #2 ApiService 缓存竞态条件
|
||
|
||
- **状态**: ✅ 已修复
|
||
- **优先级**: P0
|
||
- **文件**: `lib/src/services/api/api_service.dart`
|
||
- **触发路径**:
|
||
1. `ApiService._internal()` 构造函数中调用 `_initCacheAsync()` (fire-and-forget)
|
||
2. `_cacheOptions` 是 `late CacheOptions`,在 `_initCacheAsync()` 完成前未赋值
|
||
3. 如果首次网络请求在缓存初始化完成前发起 → 访问未初始化的 `late` 字段 → 闪退
|
||
- **修复方案**:
|
||
- `late CacheOptions` 改为 `CacheOptions?`
|
||
- 添加 `Completer<void>` 跟踪初始化状态
|
||
- `_ensureCacheReady()` 等待 Completer 完成
|
||
- `_tryGetCache()` 加 null 检查
|
||
- **修复日期**: 2026-04-09
|
||
|
||
---
|
||
|
||
### #3 Get.find 未注册 Controller 闪退
|
||
|
||
- **状态**: ✅ 已修复
|
||
- **优先级**: P1
|
||
- **文件**: `lib/src/controllers/what_to_eat_controller.dart`
|
||
- **触发路径**:
|
||
1. `_fetchSmartWithPreferences()` 中调用 `Get.find<PreferenceController>()`
|
||
2. 如果 `PreferenceController` 未被 `Get.put` 注册 → 抛出异常 → 闪退
|
||
- **修复方案**: 使用 `Get.isRegistered<T>()` 先判断再 `find`
|
||
- **修复日期**: 2026-04-09
|
||
|
||
---
|
||
|
||
### #4 Platform API 在 Web 平台崩溃
|
||
|
||
- **状态**: ✅ 已修复
|
||
- **优先级**: P1
|
||
- **文件**: `lib/src/utils/platform_utils.dart`
|
||
- **触发路径**:
|
||
1. 直接使用 `Platform.isIOS`、`Platform.isAndroid` 等
|
||
2. 在 Web 平台调用 → `UnsupportedError` → 闪退
|
||
- **修复方案**:
|
||
- 使用条件导入 `import 'dart:io' if (dart.library.html) 'platform_web_stub.dart'`
|
||
- 所有 `Platform.*` 调用前加 `if (kIsWeb) return false;`
|
||
- 创建 `platform_web_stub.dart` 提供 Web 平台 stub
|
||
- **修复日期**: 2026-04-09
|
||
|
||
---
|
||
|
||
## 🟠 中危问题(可能导致卡死)
|
||
|
||
### #5 网络请求无超时兜底
|
||
|
||
- **状态**: ✅ 已修复
|
||
- **优先级**: P2
|
||
- **文件**: `lib/src/services/api/api_service.dart`
|
||
- **触发路径**:
|
||
1. `_isOffline()` 调用 `Connectivity().checkConnectivity()`
|
||
2. 该方法本身可能卡住(如网络权限未授予)
|
||
3. 虽然 Dio 设置了 10s 超时,但连接性检查无超时
|
||
- **修复方案**: 给 `_isOffline()` 加 `.timeout(Duration(seconds: 3))` 限制,超时默认视为离线
|
||
- **修复日期**: 2026-04-09
|
||
|
||
---
|
||
|
||
### #6 `runWithLoading` 嵌套调用状态错乱
|
||
|
||
- **状态**: ✅ 已修复
|
||
- **优先级**: P2
|
||
- **文件**: `lib/src/controllers/base/base_controller.dart`
|
||
- **触发路径**:
|
||
1. `isLoading` 是单一 `RxBool`
|
||
2. 如果外层 `runWithLoading` 内部又调用 `runWithLoading`
|
||
3. 内层完成后 `isLoading = false`,但外层仍在执行
|
||
4. UI 显示加载完成,但数据可能不完整 → 用户操作异常
|
||
- **修复方案**: 使用计数器 `_loadingCount` 替代布尔值,仅当计数归零时设 `isLoading = false`
|
||
- **修复日期**: 2026-04-09
|
||
|
||
---
|
||
|
||
### #7 Hive Box 同步操作阻塞主线程
|
||
|
||
- **状态**: ✅ 已修复(低优先级标记)
|
||
- **优先级**: P3
|
||
- **文件**: `lib/src/services/data/hive_service.dart`
|
||
- **触发路径**:
|
||
1. `getMealRecordsByDate()`、`getWeeklyCalories()` 等同步遍历 Box
|
||
2. 数据量大时(如数百条记录)阻塞 UI 线程
|
||
3. 用户感觉"卡死"
|
||
- **修复方案**:
|
||
- 短期: 当前数据量不大,影响可忽略
|
||
- 长期: 改为 `compute` 或 `Isolate` 执行重计算
|
||
- **修复日期**: 2026-04-09(标记为低优先级,暂不需要 Isolate)
|
||
|
||
---
|
||
|
||
## 🟡 低危问题(边界情况)
|
||
|
||
### #8 SharedPreferences 初始化前访问
|
||
|
||
- **状态**: ✅ 已修复
|
||
- **优先级**: P3
|
||
- **文件**: `lib/src/services/data/storage_service.dart`
|
||
- **触发路径**:
|
||
1. `_prefs` 是 `late SharedPreferences`
|
||
2. 如果 `init()` 未完成就调用 `getString()` 等 → `LateInitializationError`
|
||
- **修复方案**:
|
||
- `late SharedPreferences` 改为 `SharedPreferences?`
|
||
- 添加 `isInitialized` 标志
|
||
- 读取方法用 `_prefs?.` 安全调用,返回 null
|
||
- 写入方法加 `if (_prefs == null) return;` 静默跳过
|
||
- **修复日期**: 2026-04-09
|
||
|
||
---
|
||
|
||
### #9 LoggerService 初始化前写日志
|
||
|
||
- **状态**: ✅ 已修复
|
||
- **优先级**: P3
|
||
- **文件**: `lib/src/services/log/logger_service.dart`
|
||
- **修复内容**: `_logger` 改为 `Logger?`,所有调用处加 null 检查,降级到 `debugPrint`
|
||
- **修复日期**: 2026-04-09
|
||
|
||
---
|
||
|
||
### #10 PageRoute 中间件拦截循环
|
||
|
||
- **状态**: ✅ 已修复
|
||
- **优先级**: P3
|
||
- **文件**: `lib/src/standards/route_middleware.dart`
|
||
- **触发路径**:
|
||
1. 如果 `/standards-violation` 页面自身也校验失败
|
||
2. 中间件再次重定向到 `/standards-violation` → 无限循环
|
||
- **修复方案**: 对 `/standards-violation` 路由跳过校验,直接 `return null`
|
||
- **修复日期**: 2026-04-09
|
||
|
||
---
|
||
|
||
### #11 MediaQuery 在 Context 不完整时崩溃
|
||
|
||
- **状态**: ✅ 已修复
|
||
- **优先级**: P0
|
||
- **文件**: `lib/src/standards/page_standards.dart`
|
||
- **修复内容**: 所有 `MediaQuery.of(context)` 调用加 `try-catch`,降级到默认值
|
||
- **修复日期**: 2026-04-09
|
||
|
||
---
|
||
|
||
## 📋 修复进度
|
||
|
||
| # | 问题 | 优先级 | 状态 | 修复日期 |
|
||
|---|------|--------|------|----------|
|
||
| 1 | Hive late Box 未初始化 | P0 | ✅ 已修复 | 2026-04-09 |
|
||
| 2 | ApiService 缓存竞态 | P0 | ✅ 已修复 | 2026-04-09 |
|
||
| 3 | Get.find 未注册 Controller | P1 | ✅ 已修复 | 2026-04-09 |
|
||
| 4 | Platform API Web 崩溃 | P1 | ✅ 已修复 | 2026-04-09 |
|
||
| 5 | 网络请求无超时兜底 | P2 | ✅ 已修复 | 2026-04-09 |
|
||
| 6 | runWithLoading 嵌套 | P2 | ✅ 已修复 | 2026-04-09 |
|
||
| 7 | Hive 同步操作阻塞 | P3 | ✅ 标记低优 | 2026-04-09 |
|
||
| 8 | SharedPreferences 未初始化 | P3 | ✅ 已修复 | 2026-04-09 |
|
||
| 9 | LoggerService 未初始化 | P3 | ✅ 已修复 | 2026-04-09 |
|
||
| 10 | 中间件拦截循环 | P3 | ✅ 已修复 | 2026-04-09 |
|
||
| 11 | MediaQuery 空值崩溃 | P0 | ✅ 已修复 | 2026-04-09 |
|
||
|
||
---
|
||
|
||
## 🔧 修复详情
|
||
|
||
### 2026-04-09 修复批次(第二轮)
|
||
|
||
1. **Hive late Box 未初始化** (#1)
|
||
- `late Box<T>` → `Box<T>?`,getter 加 `_assertInitialized()`
|
||
- `MealRecordController` 所有 Hive 读取加 `if (!hive.isInitialized) return;`
|
||
|
||
2. **ApiService 缓存竞态** (#2)
|
||
- `late CacheOptions` → `CacheOptions?`
|
||
- 添加 `Completer<void>` 跟踪初始化
|
||
- `_ensureCacheReady()` 等待 Completer
|
||
- `_tryGetCache()` 加 null 检查
|
||
|
||
3. **Get.find 未注册 Controller** (#3)
|
||
- `Get.find<T>()` 前加 `Get.isRegistered<T>()` 判断
|
||
|
||
4. **Platform API Web 崩溃** (#4)
|
||
- 条件导入 `dart:io if (dart.library.html)`
|
||
- 所有 `Platform.*` 调用前加 `kIsWeb` 检查
|
||
- 创建 `platform_web_stub.dart`
|
||
|
||
5. **网络请求超时兜底** (#5)
|
||
- `_isOffline()` 加 `.timeout(Duration(seconds: 3))`
|
||
- 超时默认视为离线
|
||
|
||
6. **runWithLoading 嵌套** (#6)
|
||
- `RxBool` → 计数器 `_loadingCount`
|
||
- 仅当计数归零时设 `isLoading = false`
|
||
|
||
7. **SharedPreferences 未初始化** (#8)
|
||
- `late SharedPreferences` → `SharedPreferences?`
|
||
- 读取用 `?.` 安全调用,写入加 null 检查
|
||
|
||
8. **中间件拦截循环** (#10)
|
||
- `/standards-violation` 路由跳过校验
|
||
|
||
### 2026-04-09 修复批次(第一轮)
|
||
|
||
1. **LoggerService 空值崩溃** (#9, #11 相关)
|
||
- `_logger` 改为 `Logger?`,所有方法加 null 检查
|
||
- `dispose()` 改为 `_logger?.close()`
|
||
|
||
2. **PageStandards MediaQuery 空值崩溃** (#11)
|
||
- 所有 `MediaQuery.of(context)` 加 try-catch
|
||
- 降级到安全默认值 (375×812, 44/34 padding)
|
||
- `l10n` 改为可空 `AppLocalizations?`
|
||
|
||
3. **l10n 可空类型连锁修复**
|
||
- `empty_state.dart`: `l10n?.noData ?? '暂无数据'`
|
||
- `error_state.dart`: `l10n?.retry ?? '重试'`
|
||
- `standard_dialog.dart`: 6处 `l10n?.confirm/cancel` 加降级中文
|
||
- `page_validator.dart`: `l10n?.appTitle.isNotEmpty ?? false`
|
||
|
||
4. **代码警告清理** (15项)
|
||
- 移除未使用的 import / 变量 / 不必要的 `!` / 死代码
|