memory_monitor.dart 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // memory_monitor.dart
  2. // 强化版内存监控工具类
  3. import 'dart:async';
  4. import 'dart:io';
  5. import 'package:flutter/foundation.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:logging/logging.dart';
  8. import 'package:puzzleweave/firebase/firebase_helper.dart';
  9. import 'package:puzzleweave/models/download.dart';
  10. final Logger _log = Logger('MemoryMonitor');
  11. /// 内存阶段定义
  12. enum MemoryState { normal, warning, critical }
  13. class MemoryMonitor with WidgetsBindingObserver {
  14. static final MemoryMonitor _instance = MemoryMonitor._internal();
  15. factory MemoryMonitor() => _instance;
  16. MemoryMonitor._internal();
  17. Timer? _monitorTimer;
  18. int _warningCount = 0;
  19. // 防止系统内存压力信号死循环
  20. DateTime? _lastSystemPressureTime;
  21. static const _systemPressureCooldown = Duration(seconds: 10);
  22. // 动态生成的阈值
  23. double _warningThreshold = 180.0;
  24. double _criticalThreshold = 250.0;
  25. VoidCallback? _onHighMemory;
  26. VoidCallback? _onCriticalMemory;
  27. // Release模式下的内存显示
  28. static final ValueNotifier<String> memoryDisplay = ValueNotifier<String>('Memory: 0MB');
  29. /// 初始化并启动监控
  30. void startMonitoring({Duration interval = const Duration(seconds: 30), VoidCallback? onHighMemory, VoidCallback? onCriticalMemory}) {
  31. _onHighMemory = onHighMemory;
  32. _onCriticalMemory = onCriticalMemory;
  33. // 1. 注册系统内存压力监听 (WidgetsBindingObserver)
  34. WidgetsBinding.instance.addObserver(this);
  35. // 2. 根据设备情况初始化动态阈值
  36. _initializeDynamicThresholds();
  37. // 3. 启动定时轮询
  38. _monitorTimer?.cancel();
  39. _monitorTimer = Timer.periodic(interval, (_) => _checkMemory());
  40. _log.info('Memory monitoring started. Warning: ${_warningThreshold}MB, Critical: ${_criticalThreshold}MB');
  41. }
  42. /// 停止监控
  43. void stopMonitoring() {
  44. WidgetsBinding.instance.removeObserver(this);
  45. _monitorTimer?.cancel();
  46. _log.info('Memory monitoring stopped');
  47. }
  48. /// 这是进程被杀前的最后一次清理机会
  49. @override
  50. void didHaveMemoryPressure() {
  51. final now = DateTime.now();
  52. // 防止频繁触发,增加冷却时间
  53. if (_lastSystemPressureTime != null && now.difference(_lastSystemPressureTime!) < _systemPressureCooldown) {
  54. _log.info('System memory pressure ignored (cooldown)');
  55. return;
  56. }
  57. _lastSystemPressureTime = now;
  58. _log.severe('!!! SYSTEM MEMORY PRESSURE: ${getCurrentMemoryMB().toStringAsFixed(1)}MB !!!');
  59. _emergencyCleanup(reason: 'system_pressure');
  60. _reportMemoryEvent('system_low_memory_signal', getCurrentMemoryMB());
  61. }
  62. /// 动态计算阈值:防止在低端机上设置过高,或在高端机上太敏感
  63. void _initializeDynamicThresholds() {
  64. if (Platform.isAndroid) {
  65. if (kDebugMode) {
  66. // Debug模式更保守,避免频繁清理
  67. _warningThreshold = 400.0;
  68. _criticalThreshold = 500.0;
  69. } else {
  70. // Release模式预计内存减半
  71. _warningThreshold = 120.0;
  72. _criticalThreshold = 160.0;
  73. }
  74. } else if (Platform.isIOS) {
  75. if (kDebugMode) {
  76. _warningThreshold = 300.0;
  77. _criticalThreshold = 400.0;
  78. } else {
  79. _warningThreshold = 150.0;
  80. _criticalThreshold = 200.0;
  81. }
  82. } else {
  83. _warningThreshold = 500.0;
  84. _criticalThreshold = 700.0;
  85. }
  86. }
  87. void _checkMemory() {
  88. if (!Platform.isAndroid && !Platform.isIOS) return;
  89. try {
  90. final memoryMB = getCurrentMemoryMB();
  91. // 更新UI显示
  92. memoryDisplay.value = 'Memory: ${memoryMB.toStringAsFixed(1)}MB';
  93. // 只在超过warning时才记录,减少日志噪音
  94. if (memoryMB > _warningThreshold) {
  95. _log.info('Memory: ${memoryMB.toStringAsFixed(1)} MB');
  96. }
  97. if (memoryMB > _criticalThreshold) {
  98. _handleCriticalMemory(memoryMB);
  99. } else if (memoryMB > _warningThreshold) {
  100. _handleHighMemory(memoryMB);
  101. } else {
  102. _warningCount = 0;
  103. }
  104. } catch (e) {
  105. _log.warning('Memory check failed: $e');
  106. }
  107. }
  108. void _handleCriticalMemory(double memoryMB) {
  109. _log.severe('CRITICAL memory usage: ${memoryMB.toStringAsFixed(1)} MB');
  110. _warningCount++;
  111. _onCriticalMemory?.call();
  112. _emergencyCleanup(reason: 'critical_threshold');
  113. _reportMemoryEvent('critical_memory_usage', memoryMB);
  114. }
  115. void _handleHighMemory(double memoryMB) {
  116. _log.warning('HIGH memory usage: ${memoryMB.toStringAsFixed(1)} MB');
  117. _warningCount++;
  118. _onHighMemory?.call();
  119. _reportMemoryEvent('high_memory_usage', memoryMB);
  120. }
  121. /// 核心清理逻辑:物理释放显存和缓存
  122. void _emergencyCleanup({String reason = 'unknown'}) {
  123. final beforeMB = getCurrentMemoryMB();
  124. _log.warning('Emergency cleanup: ${beforeMB.toStringAsFixed(1)}MB ($reason)');
  125. try {
  126. // 清理顺序很重要,先清理大头
  127. Download().clearAllData();
  128. PaintingBinding.instance.imageCache.clear();
  129. PaintingBinding.instance.imageCache.clearLiveImages();
  130. PaintingBinding.instance.handleMemoryPressure();
  131. // 立即检查效果
  132. final afterMB = getCurrentMemoryMB();
  133. final freedMB = beforeMB - afterMB;
  134. _log.info('Cleanup result: ${afterMB.toStringAsFixed(1)}MB (${freedMB > 0 ? "freed" : "increased"}: ${freedMB.abs().toStringAsFixed(1)}MB)');
  135. } catch (e) {
  136. _log.severe('Emergency cleanup failed: $e');
  137. }
  138. }
  139. void _reportMemoryEvent(String eventName, double memoryMB) {
  140. if (kReleaseMode) {
  141. FirebaseHelper.logEvent(eventName, {'memory_mb': memoryMB.round(), 'warning_count': _warningCount, 'threshold_c': _criticalThreshold.round()});
  142. }
  143. }
  144. static double getCurrentMemoryMB() {
  145. try {
  146. // RSS 包含代码段、栈、堆以及共享库。
  147. // 在三星等低端机上,这是判断 OOM 最直接的指标。
  148. return ProcessInfo.currentRss / (1024 * 1024);
  149. } catch (e) {
  150. return 0;
  151. }
  152. }
  153. /// 供外部在关键节点(如播广告前)手动清理
  154. void manualCleanup() {
  155. _emergencyCleanup(reason: 'manual_call');
  156. }
  157. static void logMemoryUsage(String label) {
  158. final memoryMB = getCurrentMemoryMB();
  159. memoryDisplay.value = '[$label] ${memoryMB.toStringAsFixed(1)}MB';
  160. _log.info('[$label] RSS Memory: ${memoryMB.toStringAsFixed(1)} MB');
  161. }
  162. /// 获取内存显示组件(供Release模式使用)
  163. static Widget getMemoryWidget() {
  164. return ValueListenableBuilder<String>(
  165. valueListenable: memoryDisplay,
  166. builder: (context, value, child) {
  167. return Container(
  168. padding: EdgeInsets.all(4),
  169. decoration: BoxDecoration(
  170. color: Colors.black54,
  171. borderRadius: BorderRadius.circular(4),
  172. ),
  173. child: Text(
  174. value,
  175. style: TextStyle(color: Colors.white, fontSize: 12),
  176. ),
  177. );
  178. },
  179. );
  180. }
  181. }