/** * 内存优化方案 - 实现代码 * * 优化策略: * 1. 对象池优化(减少 GC 压力) * 2. 懒加载策略(视口外卸载) * 3. 图片/资源缓存管理 * * @version 1.0.0 * @date 2026-03-07 * @author 性能优化专家 */ import 'package:flutter/material.dart'; import 'dart:collection'; import '../domain/models/core_models.dart'; // ============================================================================ // 1. 对象池优化 // ============================================================================ /// 通用对象池 class ObjectPool { final Queue _pool = Queue(); final int maxSize; final T Function() _factory; final void Function(T)? _reset; int _createdCount = 0; int _recycledCount = 0; ObjectPool({ required this.maxSize, required T Function() factory, this.reset, }) : _factory = factory; /// 从池中获取对象 T acquire() { if (_pool.isNotEmpty) { _recycledCount++; final obj = _pool.removeFirst(); _reset?.call(obj); return obj; } _createdCount++; return _factory(); } /// 归还对象到池中 void release(T obj) { if (_pool.length < maxSize) { obj.onRecycle(); _pool.add(obj); } } /// 清空池 void clear() { _pool.clear(); } /// 池统计信息 PoolStats get stats => PoolStats( createdCount: _createdCount, recycledCount: _recycledCount, poolSize: _pool.length, maxSize: maxSize, ); /// 预填充池 void prefill(int count) { for (int i = 0; i < count; i++) { _pool.add(_factory()); _createdCount++; } } } /// 池化对象接口 abstract class Poolable { void onRecycle(); } /// 池统计信息 class PoolStats { final int createdCount; final int recycledCount; final int poolSize; final int maxSize; PoolStats({ required this.createdCount, required this.recycledCount, required this.poolSize, required this.maxSize, }); double get recycleRate => createdCount + recycledCount > 0 ? recycledCount / (createdCount + recycledCount) : 0.0; @override String toString() { return 'PoolStats(created: $createdCount, recycled: $recycledCount, ' 'pool: $poolSize/$maxSize, rate: ${(recycleRate * 100).toStringAsFixed(1)}%)'; } } // ============================================================================ // 1.1 元件对象池 // ============================================================================ /// 可池化的元件数据 class PooledComponentData implements Poolable { ID id = ''; String name = ''; Position2D position = const Position2D(x: 0, y: 0); int rotation = 0; List pins = []; bool isSelected = false; bool isHovered = false; @override void onRecycle() { id = ''; name = ''; position = const Position2D(x: 0, y: 0); rotation = 0; pins = []; isSelected = false; isHovered = false; } void copyFrom(Component component) { id = component.id; name = component.name; position = component.position; rotation = component.rotation; pins = component.pins; } } /// 元件对象池管理器 class ComponentObjectPool { final ObjectPool _pool; ComponentObjectPool({int poolSize = 200}) : _pool = ObjectPool( maxSize: poolSize, factory: () => PooledComponentData(), ); PooledComponentData acquire() => _pool.acquire(); void release(PooledComponentData data) => _pool.release(data); /// 批量获取 List acquireBatch(int count) { return List.generate(count, (_) => acquire()); } /// 批量归还 void releaseBatch(Iterable items) { for (final item in items) { release(item); } } PoolStats get stats => _pool.stats; void prefill(int count) => _pool.prefill(count); void clear() => _pool.clear(); } // ============================================================================ // 1.2 Paint 对象池(减少 CustomPainter 分配) // ============================================================================ /// Paint 对象池 class PaintObjectPool { final Map> _paintPools = {}; final int _maxSizePerType; PaintObjectPool({int maxSizePerType = 50}) : _maxSizePerType = maxSizePerType; Paint acquire(String type) { _paintPools.putIfAbsent(type, () => Queue()); final pool = _paintPools[type]!; if (pool.isNotEmpty) { return pool.removeFirst(); } return Paint(); } void release(String type, Paint paint) { _paintPools.putIfAbsent(type, () => Queue()); final pool = _paintPools[type]!; if (pool.length < _maxSizePerType) { // 重置 Paint 状态 paint ..color = const Color(0xFF000000) ..strokeWidth = 1.0 ..style = PaintingStyle.fill; pool.add(paint); } } void clear() { _paintPools.clear(); } } // ============================================================================ // 2. 懒加载策略 // ============================================================================ /// 视口配置 class ViewportConfig { final double x; final double y; final double width; final double height; final double zoomLevel; final double margin; const ViewportConfig({ required this.x, required this.y, required this.width, required this.height, required this.zoomLevel, this.margin = 100.0, }); /// 获取可见区域(带边距) Rect get bounds => Rect.fromLTWH( x - margin / zoomLevel, y - margin / zoomLevel, width + (2 * margin / zoomLevel), height + (2 * margin / zoomLevel), ); /// 检查点是否在视口内 bool containsPoint(double px, double py) { return px >= bounds.left && px <= bounds.right && py >= bounds.top && py <= bounds.bottom; } /// 检查矩形是否与视口相交 bool intersectsRect(Rect rect) { return bounds.overlaps(rect); } } /// 懒加载缓存 class LazyLoadCache { final Map _loadedComponents = {}; final Set _pendingLoads = {}; final int _maxLoadedComponents; final Queue _accessOrder = Queue(); LazyLoadCache({int maxLoadedComponents = 500}) : _maxLoadedComponents = maxLoadedComponents; /// 获取已加载的元件 Component? get(ID id) => _loadedComponents[id]; /// 检查是否已加载 bool isLoaded(ID id) => _loadedComponents.containsKey(id); /// 标记为已加载 void set(ID id, Component component) { // 如果超出容量,移除最久未使用的 while (_loadedComponents.length >= _maxLoadedComponents) { final oldestId = _accessOrder.removeFirst(); _loadedComponents.remove(oldestId); } _loadedComponents[id] = component; _accessOrder.remove(id); _accessOrder.addLast(id); } /// 批量加载 void loadAll(Map components) { for (final entry in components.entries) { set(entry.key, entry.value); } } /// 卸载视口外的元件 void unloadOutsideViewport(ViewportConfig viewport) { final toUnload = []; _loadedComponents.forEach((id, component) { final posX = component.position.x.toDouble(); final posY = component.position.y.toDouble(); if (!viewport.containsPoint(posX, posY)) { toUnload.add(id); } }); for (final id in toUnload) { _loadedComponents.remove(id); _accessOrder.remove(id); } debugPrint('懒加载:卸载了 ${toUnload.length} 个视口外元件'); } /// 预加载视口附近的元件 void preloadNearby( Map allComponents, ViewportConfig viewport, ) { final preloadMargin = viewport.margin * 2; final preloadBounds = Rect.fromLTWH( viewport.bounds.left - preloadMargin, viewport.bounds.top - preloadMargin, viewport.bounds.width + (2 * preloadMargin), viewport.bounds.height + (2 * preloadMargin), ); for (final entry in allComponents.entries) { if (_loadedComponents.containsKey(entry.key)) continue; final posX = entry.value.position.x.toDouble(); final posY = entry.value.position.y.toDouble(); if (preloadBounds.contains(Offset(posX, posY))) { set(entry.key, entry.value); } } } int get loadedCount => _loadedComponents.length; void clear() { _loadedComponents.clear(); _accessOrder.clear(); } } // ============================================================================ // 3. 图片/资源缓存管理 // ============================================================================ /// 图片缓存配置 class ImageCacheConfig { final int maxMemoryBytes; final int maxItemCount; final Duration expiryDuration; const ImageCacheConfig({ this.maxMemoryBytes = 50 * 1024 * 1024, // 50MB this.maxItemCount = 200, this.expiryDuration = const Duration(minutes: 5), }); } /// 缓存的图片 class CachedImage { final ImageProvider provider; final int sizeBytes; final DateTime loadedAt; int accessCount = 0; DateTime lastAccessedAt; CachedImage({ required this.provider, required this.sizeBytes, required this.loadedAt, }) : lastAccessedAt = loadedAt; void access() { accessCount++; lastAccessedAt = DateTime.now(); } bool get isExpired { return DateTime.now().difference(loadedAt) > const Duration(minutes: 5); } } /// 图片缓存管理器 class ImageCacheManager { final Map _cache = {}; final ImageCacheConfig _config; int _currentMemoryUsage = 0; ImageCacheManager({ImageCacheConfig? config}) : _config = config ?? const ImageCacheConfig(); /// 获取图片 ImageProvider? get(String key) { final cached = _cache[key]; if (cached == null) return null; cached.access(); return cached.provider; } /// 缓存图片 void put(String key, ImageProvider provider, {int sizeBytes = 0}) { if (_cache.containsKey(key)) { _cache[key]!.access(); return; } // 检查内存限制 while (_currentMemoryUsage + sizeBytes > _config.maxMemoryBytes || _cache.length >= _config.maxItemCount) { _evictLeastUsed(); } _cache[key] = CachedImage( provider: provider, sizeBytes: sizeBytes, loadedAt: DateTime.now(), ); _currentMemoryUsage += sizeBytes; } /// 移除最少使用的图片 void _evictLeastUsed() { if (_cache.isEmpty) return; String? leastUsedKey; int leastUsedScore = double.maxFinite.toInt(); _cache.forEach((key, cached) { // 评分:综合考虑访问次数和最后访问时间 final score = cached.accessCount + (DateTime.now().difference(cached.lastAccessedAt).inSeconds ~/ 10); if (score < leastUsedScore) { leastUsedScore = score; leastUsedKey = key; } }); if (leastUsedKey != null) { final removed = _cache.remove(leastUsedKey)!; _currentMemoryUsage -= removed.sizeBytes; } } /// 清除过期缓存 void clearExpired() { final expiredKeys = []; _cache.forEach((key, cached) { if (cached.isExpired) { expiredKeys.add(key); } }); for (final key in expiredKeys) { final removed = _cache.remove(key)!; _currentMemoryUsage -= removed.sizeBytes; } if (expiredKeys.isNotEmpty) { debugPrint('图片缓存:清除了 ${expiredKeys.length} 个过期项'); } } /// 清除所有缓存 void clear() { _cache.clear(); _currentMemoryUsage = 0; } /// 缓存统计 CacheStats get stats => CacheStats( itemCount: _cache.length, memoryBytes: _currentMemoryUsage, maxItems: _config.maxItemCount, maxMemory: _config.maxMemoryBytes, ); } /// 缓存统计 class CacheStats { final int itemCount; final int memoryBytes; final int maxItems; final int maxMemory; CacheStats({ required this.itemCount, required this.memoryBytes, required this.maxItems, required this.maxMemory, }); double get memoryUsagePercent => maxMemory > 0 ? (memoryBytes / maxMemory * 100) : 0.0; double get itemUsagePercent => maxItems > 0 ? (itemCount / maxItems * 100) : 0.0; @override String toString() { return 'CacheStats(items: $itemCount/$maxItems (${itemUsagePercent.toStringAsFixed(1)}%), ' 'memory: ${(memoryBytes / 1024 / 1024).toStringAsFixed(2)}MB/${(maxMemory / 1024 / 1024)}MB ' '(${memoryUsagePercent.toStringAsFixed(1)}%))'; } } // ============================================================================ // 4. 优化的画布渲染器(集成所有优化) // ============================================================================ /// 优化的画布渲染配置 class OptimizedCanvasConfig { final bool enableObjectPooling; final bool enableLazyLoading; final bool enableImageCaching; final int componentPoolSize; final int maxLoadedComponents; final ImageCacheConfig imageCacheConfig; const OptimizedCanvasConfig({ this.enableObjectPooling = true, this.enableLazyLoading = true, this.enableImageCaching = true, this.componentPoolSize = 200, this.maxLoadedComponents = 500, this.imageCacheConfig = const ImageCacheConfig(), }); } /// 优化的画布渲染器 class OptimizedCanvasRenderer { final OptimizedCanvasConfig _config; // 对象池 late ComponentObjectPool _componentPool; late PaintObjectPool _paintPool; // 懒加载缓存 late LazyLoadCache _lazyLoadCache; // 图片缓存 late ImageCacheManager _imageCache; // 当前视口 ViewportConfig? _currentViewport; OptimizedCanvasRenderer({OptimizedCanvasConfig? config}) : _config = config ?? const OptimizedCanvasConfig() { _initialize(); } void _initialize() { if (_config.enableObjectPooling) { _componentPool = ComponentObjectPool( poolSize: _config.componentPoolSize, ); _paintPool = PaintObjectPool(); } if (_config.enableLazyLoading) { _lazyLoadCache = LazyLoadCache( maxLoadedComponents: _config.maxLoadedComponents, ); } if (_config.enableImageCaching) { _imageCache = ImageCacheManager( config: _config.imageCacheConfig, ); } } /// 更新视口 void updateViewport(ViewportConfig viewport) { _currentViewport = viewport; if (_config.enableLazyLoading) { // 卸载视口外元件 _lazyLoadCache.unloadOutsideViewport(viewport); // 预加载附近元件 // _lazyLoadCache.preloadNearby(allComponents, viewport); } } /// 获取元件(可能从懒加载缓存) Component? getComponent(ID id, Component? fullComponent) { if (!_config.enableLazyLoading) return fullComponent; final cached = _lazyLoadCache.get(id); if (cached != null) return cached; // 如果不在缓存中,加载并返回完整数据 if (fullComponent != null) { _lazyLoadCache.set(id, fullComponent); } return fullComponent; } /// 获取 Paint(从对象池) Paint getPaint(String type) { if (_config.enableObjectPooling) { return _paintPool.acquire(type); } return Paint(); } /// 归还 Paint 到对象池 void releasePaint(String type, Paint paint) { if (_config.enableObjectPooling) { _paintPool.release(type, paint); } } /// 缓存图片 void cacheImage(String key, ImageProvider provider, {int sizeBytes = 0}) { if (_config.enableImageCaching) { _imageCache.put(key, provider, sizeBytes: sizeBytes); } } /// 获取缓存的图片 ImageProvider? getCachedImage(String key) { if (!_config.enableImageCaching) return null; return _imageCache.get(key); } /// 获取性能统计 PerformanceStats get performanceStats => PerformanceStats( componentPoolStats: _config.enableObjectPooling ? _componentPool.stats : null, lazyLoadCacheSize: _config.enableLazyLoading ? _lazyLoadCache.loadedCount : 0, imageCacheStats: _config.enableImageCaching ? _imageCache.stats : null, ); /// 清理资源 void dispose() { if (_config.enableObjectPooling) { _componentPool.clear(); _paintPool.clear(); } if (_config.enableLazyLoading) { _lazyLoadCache.clear(); } if (_config.enableImageCaching) { _imageCache.clear(); } } } /// 性能统计 class PerformanceStats { final PoolStats? componentPoolStats; final int lazyLoadCacheSize; final CacheStats? imageCacheStats; PerformanceStats({ this.componentPoolStats, required this.lazyLoadCacheSize, this.imageCacheStats, }); @override String toString() { return 'PerformanceStats(\n' ' Component Pool: ${componentPoolStats ?? "disabled"},\n' ' Lazy Load Cache: $lazyLoadCacheSize components,\n' ' Image Cache: ${imageCacheStats ?? "disabled"}\n' ')'; } } // ============================================================================ // 5. 使用示例 // ============================================================================ /* /// 在 EditableCanvas 中使用优化 class OptimizedEditableCanvas extends StatefulWidget { final Design design; final Function(Design) onDesignChanged; final SelectionManager selectionManager; final OptimizedCanvasConfig config; const OptimizedEditableCanvas({ required this.design, required this.onDesignChanged, required this.selectionManager, this.config = const OptimizedCanvasConfig(), }); @override State createState() => _OptimizedEditableCanvasState(); } class _OptimizedEditableCanvasState extends State { late OptimizedCanvasRenderer _renderer; double _zoomLevel = 1.0; Offset _offset = Offset.zero; @override void initState() { super.initState(); _renderer = OptimizedCanvasRenderer(config: widget.config); // 预填充对象池 if (widget.config.enableObjectPooling) { // 预填充 100 个元件对象 } } @override void dispose() { _renderer.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { // 更新视口 _renderer.updateViewport(ViewportConfig( x: -_offset.dx / _zoomLevel, y: -_offset.dy / _zoomLevel, width: constraints.maxWidth / _zoomLevel, height: constraints.maxHeight / _zoomLevel, zoomLevel: _zoomLevel, )); return CustomPaint( painter: OptimizedSchematicPainter( design: widget.design, renderer: _renderer, zoomLevel: _zoomLevel, offset: _offset, ), ); }, ); } } */