9.8 KiB
代码闪退风险分析报告
分析日期: 2026-04-11 修复日期: 2026-04-11 分析范围: 全项目代码审查 严重程度: 🔴 高危 🟡 中危 🟢 低危
修复进度
✅ 已修复 (P0)
- WeeklyMenuController 全局注册
- BedtimeReminderController 全局注册
- 移除页面中的重复 Get.put 调用
- HiveService 添加 box 缓存机制
- BedtimeReminderController 添加时间有效性检查
⏸️ 待修复 (P2)
- 本地通知功能实现(需添加 flutter_local_notifications 依赖)
一、Controller 重复注册风险 🔴 高危
问题描述
多个页面使用 Get.put() 直接注册控制器,可能导致重复注册或生命周期混乱。
风险代码位置
1. WeeklyMenuController 重复注册
文件: lib/src/pages/tools/weekly_menu_planner_page.dart:26
final WeeklyMenuController _controller = Get.put(WeeklyMenuController());
风险:
- 该控制器未在
AppBinding中全局注册 - 每次进入页面都会重新创建实例
- 如果页面被多次推入,可能导致多个实例共存
- Hive 数据可能被不同实例覆盖
建议修复:
// 方案1: 在 AppBinding 中全局注册
// lib/src/app_binding.dart
Get.put(WeeklyMenuController(), permanent: true);
// 方案2: 使用 Get.lazyPut + fenix
class WeeklyMenuBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => WeeklyMenuController(), fenix: true);
}
}
2. BedtimeReminderController 重复注册
文件: lib/src/pages/profile/bedtime_reminder_page.dart:19
final controller = Get.put(BedtimeReminderController());
风险: 同上
建议修复:
// 在 AppBinding 中全局注册
// lib/src/app_binding.dart
Get.put(BedtimeReminderController(), permanent: true);
3. MealRecordController 重复注册
文件: lib/src/pages/home/home_page.dart:53
Get.put(MealRecordController(), permanent: true);
风险:
- 该控制器已在
NutritionBinding中注册(带检查) - 这里再次注册可能导致重复实例
建议修复: 移除 home_page.dart 中的注册,依赖 NutritionBinding 的注册逻辑
二、HiveService 通用方法风险 🔴 高危
问题描述
新添加的通用 get() 和 put() 方法直接使用 Hive.box<dynamic>(boxName) 打开 box,缺少安全检查。
风险代码位置
文件: lib/src/services/data/hive_service.dart:449-473
dynamic get(String boxName, String key) {
if (!_initialized) return null;
try {
final box = Hive.box<dynamic>(boxName); // ⚠️ box 可能不存在
return box.get(key);
} catch (e) {
LoggerService().error('HiveService get error ($boxName/$key): $e');
return null;
}
}
风险:
- 如果 box 不存在,
Hive.box<dynamic>(boxName)会抛出异常 - 虽然有 try-catch,但每次访问都会尝试打开 box,性能差
- 多个并发访问可能导致 box 重复打开
建议修复:
// 添加 box 缓存机制
final Map<String, Box<dynamic>> _dynamicBoxCache = {};
Box<dynamic> _getOrOpenBox(String boxName) {
if (_dynamicBoxCache.containsKey(boxName)) {
return _dynamicBoxCache[boxName]!;
}
try {
final box = Hive.box<dynamic>(boxName);
_dynamicBoxCache[boxName] = box;
return box;
} catch (e) {
LoggerService().error('Failed to open box $boxName: $e');
rethrow;
}
}
dynamic get(String boxName, String key) {
if (!_initialized) return null;
try {
final box = _getOrOpenBox(boxName);
return box.get(key);
} catch (e) {
LoggerService().error('HiveService get error ($boxName/$key): $e');
return null;
}
}
// 在 close() 方法中清理缓存
Future<void> close() async {
// ... 现有代码 ...
for (final box in _dynamicBoxCache.values) {
await box.close();
}
_dynamicBoxCache.clear();
_initialized = false;
}
三、空指针风险 🟡 中危
问题描述
多处代码缺少空值检查,可能导致空指针异常。
风险代码位置
1. WeeklyMenuController 数据访问
文件: lib/src/controllers/weekly_menu_controller.dart:111-114
void addMealToDay(String dateKey, String mealType, RecipeModel recipe) {
if (currentMenu.value == null) return;
final dayMenu = currentMenu.value!.dailyMenus[dateKey];
if (dayMenu == null) return; // ✅ 有检查
// ...
}
评价: 此处有检查,但其他类似方法可能遗漏
2. BedtimeReminderController 时间计算
文件: lib/src/controllers/bedtime_reminder_controller.dart:169-187
bool shouldShowBeforeSleepEatingWarning() {
if (!beforeSleepEatingReminder.value) return false;
final now = DateTime.now();
final dinnerTime = DateTime(
now.year,
now.month,
now.day,
dinnerHour.value,
dinnerMinute.value,
);
// ⚠️ 如果 dinnerHour/dinnerMinute 超出有效范围(如 25:70),DateTime 构造会抛异常
// ...
}
建议修复:
bool shouldShowBeforeSleepEatingWarning() {
if (!beforeSleepEatingReminder.value) return false;
// 添加时间有效性检查
if (dinnerHour.value < 0 || dinnerHour.value > 23 ||
dinnerMinute.value < 0 || dinnerMinute.value > 59) {
debugPrint('Invalid dinner time: ${dinnerHour.value}:${dinnerMinute.value}');
return false;
}
final now = DateTime.now();
final dinnerTime = DateTime(
now.year,
now.month,
now.day,
dinnerHour.value,
dinnerMinute.value,
);
// ...
}
四、TODO 未完成功能 🟡 中危
问题描述
代码中存在 TODO 标记的功能未实现,可能导致功能不完整。
未完成功能
1. 本地通知功能
文件: lib/src/controllers/bedtime_reminder_controller.dart:126-129
if (enabled) {
ToastService.show(message: '已启用就寝提醒 🔔');
// TODO: 实际设置本地通知
} else {
ToastService.show(message: '已关闭就寝提醒 🔕');
// TODO: 取消本地通知
}
影响:
- 用户启用提醒后不会收到实际通知
- 用户体验不完整
建议实现:
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class BedtimeReminderController extends BaseController {
final FlutterLocalNotificationsPlugin _notifications =
FlutterLocalNotificationsPlugin();
@override
void onInit() {
super.onInit();
_initNotifications();
_loadSettings();
_calculateRecommendedBedtime();
}
Future<void> _initNotifications() async {
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
const iosSettings = DarwinInitializationSettings();
const settings = InitializationSettings(
android: androidSettings,
iOS: iosSettings,
);
await _notifications.initialize(settings);
}
Future<void> _scheduleNotification() async {
final now = DateTime.now();
final bedtime = DateTime(
now.year,
now.month,
now.day,
recommendedBedtimeHour.value,
recommendedBedtimeMinute.value,
);
if (bedtime.isBefore(now)) {
bedtime = bedtime.add(Duration(days: 1));
}
await _notifications.zonedSchedule(
'bedtime_reminder',
'就寝提醒',
controller.bedtimeRecommendation,
tz.TZDateTime.from(bedtime, tz.local),
const NotificationDetails(
android: AndroidNotificationDetails(
'bedtime_reminder_channel',
'就寝提醒',
importance: Importance.high,
),
iOS: DarwinNotificationDetails(),
),
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
);
}
void toggleReminder(bool enabled) {
reminderEnabled.value = enabled;
_saveSettings();
if (enabled) {
ToastService.show(message: '已启用就寝提醒 🔔');
_scheduleNotification();
} else {
ToastService.show(message: '已关闭就寝提醒 🔕');
_notifications.cancel('bedtime_reminder');
}
}
}
五、代码不足与改进建议 🟢 低危
1. 缺少输入验证
问题: 用户输入的时间、数值等缺少范围验证
建议:
- 在 Controller 中添加数据验证方法
- 在 UI 层添加输入限制(如 CupertinoPicker 的范围)
- 添加错误提示
2. 缺少日志记录
问题: 关键操作缺少日志记录,不利于问题排查
建议:
- 在数据保存/加载时添加日志
- 在异常处理时记录详细错误信息
- 使用 LoggerService 替代 debugPrint
3. 缺少数据迁移机制
问题: Hive 数据结构变更时缺少迁移机制
建议:
- 为每个 box 添加 schema 版本号
- 实现数据迁移逻辑
- 参考现有的
_runMigrations()方法扩展
4. 缺少单元测试
问题: 关键业务逻辑缺少单元测试
建议:
- 为 Controller 添加单元测试
- 为 Service 添加单元测试
- 为工具类添加单元测试
六、优先级修复建议
🔴 立即修复(P0)
- Controller 重复注册问题 - 可能导致数据丢失和内存泄漏
- HiveService 通用方法风险 - 可能导致数据读写失败
🟡 尽快修复(P1)
- 空指针风险 - 添加时间有效性检查
- 本地通知功能实现 - 完善用户体验
🟢 计划修复(P2)
- 输入验证 - 提升代码健壮性
- 日志记录 - 便于问题排查
- 单元测试 - 保证代码质量
七、总结
本次代码审查发现的主要问题:
- Controller 管理混乱 - 多处重复注册,需要统一管理
- HiveService 安全性不足 - 通用方法缺少 box 缓存和错误处理
- 功能不完整 - 本地通知等 TODO 未实现
- 缺少防御性编程 - 输入验证、空值检查不足
建议优先修复 P0 和 P1 级别问题,然后逐步完善 P2 级别改进。