# 内存管理优化指南 ## 问题分析 当前应用的内存问题主要集中在以下几个方面: 1. **ui.Image 对象未及时释放** - 每个关卡占用 15-30MB 2. **DownloadItem 持有原始图片数据** - 额外占用 5-15MB 3. **AnimationController 泄漏** - 每个页面 7 个控制器 4. **Picture 对象累积** - backgroundPicture 和 cardPicture --- ## 优化方案 1: Board 资源立即释放 ### 当前代码 (board_play.dart) ```dart class _BoardPlayState extends State { Board? board; @override void dispose() { board?.dispose(); // ⚠️ 可能太晚 super.dispose(); } void _onSuccess() { data.workDone(widget.item); // ❌ 资源仍然占用内存 } } ``` ### 优化后代码 ```dart class _BoardPlayState extends State { Board? board; Timer? _resourceCleanupTimer; @override void dispose() { _resourceCleanupTimer?.cancel(); _cleanupBoardResources(); super.dispose(); } void _cleanupBoardResources() { if (board != null) { _log.info('Cleaning up board resources'); board!.dispose(); board = null; } } void _onSuccess() { _log.info('Level completed, scheduling resource cleanup'); data.workDone(widget.item); // ✅ 延迟 2 秒后释放资源(等待动画完成) _resourceCleanupTimer = Timer(Duration(seconds: 2), () { if (mounted) { _cleanupBoardResources(); // ✅ 强制触发垃圾回收(仅在 debug 模式) if (kDebugMode) { _log.info('Requesting garbage collection'); } } }); } // ✅ 页面退出时立即清理 void _onWillPop(bool didPop, dynamic result) async { _log.info('Page popping, cleaning up resources'); _cleanupBoardResources(); if (!didPop && mounted) { Navigator.of(context).pop(); } } } ``` --- ## 优化方案 2: Board.dispose() 增强 ### 当前代码 (board.dart) ```dart class Board { final ui.Image image; ui.Picture? backgroundPicture; ui.Picture? cardPicture; dispose() { backgroundPicture?.dispose(); backgroundPicture = null; cardPicture?.dispose(); cardPicture = null; image.dispose(); // ⚠️ 最后才释放 boardNotifier.dispose(); } } ``` ### 优化后代码 ```dart class Board { final ui.Image image; ui.Picture? backgroundPicture; ui.Picture? cardPicture; bool _isDisposed = false; dispose() { if (_isDisposed) { _log.warning('Board already disposed'); return; } _log.info('Disposing board resources'); _isDisposed = true; // ✅ 按照占用内存大小顺序释放(先释放大的) // 1. 释放 Image (最大,10-20MB) try { image.dispose(); _log.info('Image disposed'); } catch (e) { _log.warning('Failed to dispose image: $e'); } // 2. 释放 Pictures (中等,5-10MB) try { backgroundPicture?.dispose(); backgroundPicture = null; _log.info('Background picture disposed'); } catch (e) { _log.warning('Failed to dispose background picture: $e'); } try { cardPicture?.dispose(); cardPicture = null; _log.info('Card picture disposed'); } catch (e) { _log.warning('Failed to dispose card picture: $e'); } // 3. 清空 pieces 列表 for (var piece in pieces) { piece.path = null; piece.outLinePath = null; piece.innerLinePath = null; piece.group = null; } pieces.clear(); // 4. 清空 groups backupGroups.clear(); // 5. 释放 notifier try { boardNotifier.dispose(); } catch (e) { _log.warning('Failed to dispose boardNotifier: $e'); } _log.info('Board disposal complete'); } } ``` --- ## 优化方案 3: DownloadItem 内存优化 ### 当前代码 (download.dart) ```dart class DownloadItem { Uint8List? _data; // ❌ 持有原始数据 Future ensureDataLoaded() async { if (_data != null) return _data!; final file = await localFile(cachePath); _data = await file.readAsBytes(); return _data!; } } ``` ### 优化后代码 ```dart class DownloadItem { Uint8List? _data; DateTime? _lastAccessTime; static const _dataRetentionDuration = Duration(minutes: 5); Timer? _cleanupTimer; Future ensureDataLoaded() async { _lastAccessTime = DateTime.now(); if (_data != null) { _log.info('Data cache hit for $cachePath'); _scheduleCleanup(); return _data!; } _log.info('Loading data from disk: $cachePath'); final file = await localFile(cachePath); _data = await file.readAsBytes(); // ✅ 调度自动清理 _scheduleCleanup(); return _data!; } // ✅ 自动清理未使用的数据 void _scheduleCleanup() { _cleanupTimer?.cancel(); _cleanupTimer = Timer(_dataRetentionDuration, () { if (_data != null) { final timeSinceLastAccess = DateTime.now().difference(_lastAccessTime!); if (timeSinceLastAccess >= _dataRetentionDuration) { _log.info('Auto-cleaning unused data: $cachePath'); _data = null; } } }); } // ✅ 手动清理 void clearData() { _cleanupTimer?.cancel(); _data = null; _log.info('Manually cleared data: $cachePath'); } dispose() { _isDisposed = true; _cleanupTimer?.cancel(); _data = null; subscription?.cancel(); client?.close(); } } ``` --- ## 优化方案 4: 完成关卡后清理缓存 ### 当前代码 (data.dart) ```dart class Data { void workDone(ListItem item, {Duration? timeSpent}) { final newWork = Work.fromListItem(item, timeSpent: timeSpent); final updatedWorks = [...completedWorks.value, newWork]; completedWorks.value = updatedWorks; _persistence.completedWorks = updatedWorks; _clearCompletedItemCache(item); // ✅ 已有,但可以增强 } } ``` ### 优化后代码 ```dart class Data { void workDone(ListItem item, {Duration? timeSpent}) { final newWork = Work.fromListItem(item, timeSpent: timeSpent); final updatedWorks = [...completedWorks.value, newWork]; completedWorks.value = updatedWorks; _persistence.completedWorks = updatedWorks; // ✅ 立即清理缓存 _clearCompletedItemCache(item); // ✅ 清理内存中的下载项 _clearDownloadItemFromMemory(item); } void _clearDownloadItemFromMemory(ListItem item) { if (item is RemoteItem) { try { final downloadItem = Download()._cache[ApiHelper.imageUri(item.id, 'high')]; if (downloadItem != null) { _log.info('Clearing download item from memory: ${item.id}'); downloadItem.clearData(); Download()._cache.remove(ApiHelper.imageUri(item.id, 'high')); } } catch (e) { _log.warning('Failed to clear download item: $e'); } } } void _clearCompletedItemCache(ListItem item) async { // 删除进度 JSON try { final jsonFile = await localFile(item.jsonPath); if (await jsonFile.exists()) { await jsonFile.delete(); _log.info('Cleared JSON cache: ${item.jsonPath}'); } } catch (e) { _log.severe('Failed to clear JSON cache: $e'); } // 删除图片缓存 if (item is RemoteItem) { try { final imageFile = await localFile(item.cachePath); if (await imageFile.exists()) { await imageFile.delete(); _log.info('Cleared image cache: ${item.cachePath}'); } final tmpFile = await localFile('${item.cachePath}.tmp'); if (await tmpFile.exists()) { await tmpFile.delete(); } } catch (e) { _log.severe('Failed to clear image cache: $e'); } } } } ``` --- ## 优化方案 5: 内存监控和自动清理 ### 新增工具类 (lib/utils/memory_monitor.dart) ```dart import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; final Logger _log = Logger('memory_monitor.dart'); class MemoryMonitor { static final MemoryMonitor _instance = MemoryMonitor._internal(); factory MemoryMonitor() => _instance; MemoryMonitor._internal(); Timer? _monitorTimer; int _highMemoryWarningCount = 0; // 内存阈值 (MB) static const int warningThreshold = 150; static const int criticalThreshold = 200; // 回调函数 Function()? onHighMemory; Function()? onCriticalMemory; void startMonitoring({ Duration interval = const Duration(seconds: 30), Function()? onHighMemory, Function()? onCriticalMemory, }) { this.onHighMemory = onHighMemory; this.onCriticalMemory = onCriticalMemory; _monitorTimer?.cancel(); _monitorTimer = Timer.periodic(interval, (_) => _checkMemory()); _log.info('Memory monitoring started'); } void stopMonitoring() { _monitorTimer?.cancel(); _log.info('Memory monitoring stopped'); } void _checkMemory() { if (!Platform.isAndroid && !Platform.isIOS) return; try { final rss = ProcessInfo.currentRss; final memoryMB = rss / (1024 * 1024); _log.info('Current memory usage: ${memoryMB.toStringAsFixed(1)} MB'); if (memoryMB > criticalThreshold) { _log.severe('CRITICAL memory usage: ${memoryMB.toStringAsFixed(1)} MB'); _highMemoryWarningCount++; onCriticalMemory?.call(); // 触发紧急清理 _emergencyCleanup(); } else if (memoryMB > warningThreshold) { _log.warning('HIGH memory usage: ${memoryMB.toStringAsFixed(1)} MB'); _highMemoryWarningCount++; onHighMemory?.call(); } else { _highMemoryWarningCount = 0; } // 上报到 Firebase if (kReleaseMode && memoryMB > warningThreshold) { FirebaseHelper.logEvent('high_memory_usage', { 'memory_mb': memoryMB.round(), 'threshold': memoryMB > criticalThreshold ? 'critical' : 'warning', }); } } catch (e) { _log.warning('Failed to check memory: $e'); } } void _emergencyCleanup() { _log.warning('Executing emergency memory cleanup'); try { // 1. 清理下载缓存 Download()._cache.forEach((key, item) { item.clearData(); }); // 2. 清理图片缓存(如果有全局缓存) // imageCache.clear(); // imageCache.clearLiveImages(); _log.info('Emergency cleanup completed'); } catch (e) { _log.severe('Emergency cleanup failed: $e'); } } // 获取当前内存使用情况 static double getCurrentMemoryMB() { try { final rss = ProcessInfo.currentRss; return rss / (1024 * 1024); } catch (e) { return 0; } } } // 使用示例:在 main.dart 中启动监控 /* void main() { runApp(MyApp()); // 启动内存监控 MemoryMonitor().startMonitoring( onHighMemory: () { _log.warning('High memory detected, consider cleanup'); }, onCriticalMemory: () { _log.severe('Critical memory, forcing cleanup'); }, ); } */ ``` --- ## 测试验证 ### 内存测试脚本 ```dart // 在 BoardPlay 中添加内存日志 class _BoardPlayState extends State { @override void initState() { super.initState(); _logMemoryUsage('initState'); } @override void dispose() { _logMemoryUsage('dispose (before cleanup)'); _cleanupBoardResources(); _logMemoryUsage('dispose (after cleanup)'); super.dispose(); } void _onSuccess() { _logMemoryUsage('onSuccess (before cleanup)'); data.workDone(widget.item); Timer(Duration(seconds: 2), () { _cleanupBoardResources(); _logMemoryUsage('onSuccess (after cleanup)'); }); } void _logMemoryUsage(String label) { final memoryMB = MemoryMonitor.getCurrentMemoryMB(); _log.info('[$label] Memory: ${memoryMB.toStringAsFixed(1)} MB'); } } ``` ### 预期结果 优化前: ``` [initState] Memory: 120.5 MB [onSuccess (before cleanup)] Memory: 145.2 MB [onSuccess (after cleanup)] Memory: 143.8 MB ❌ 只释放了 1.4MB [dispose (before cleanup)] Memory: 145.0 MB [dispose (after cleanup)] Memory: 144.2 MB ``` 优化后: ``` [initState] Memory: 120.5 MB [onSuccess (before cleanup)] Memory: 145.2 MB [onSuccess (after cleanup)] Memory: 125.3 MB ✅ 释放了 19.9MB [dispose (before cleanup)] Memory: 125.5 MB [dispose (after cleanup)] Memory: 105.8 MB ✅ 释放了 19.7MB ``` --- ## 总结 通过以上优化,预期可以: 1. **减少内存占用 50-70%** 2. **LMK 率从 0.84% 降至 <0.2%** 3. **Crash 率从 2.96% 降至 <1%** 4. **连续玩 10 关后内存占用 < 150MB** 关键点: - ✅ 立即释放已完成关卡的资源 - ✅ 自动清理未使用的下载数据 - ✅ 监控内存使用,触发紧急清理 - ✅ 按内存占用大小顺序释放资源