mobile-eda/lib/core/optimization/memory_optimization.dart

760 lines
19 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 内存优化方案 - 实现代码
*
* 优化策略:
* 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<T extends Poolable> {
final Queue<T> _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<PinReference> 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<PooledComponentData> _pool;
ComponentObjectPool({int poolSize = 200})
: _pool = ObjectPool(
maxSize: poolSize,
factory: () => PooledComponentData(),
);
PooledComponentData acquire() => _pool.acquire();
void release(PooledComponentData data) => _pool.release(data);
/// 批量获取
List<PooledComponentData> acquireBatch(int count) {
return List.generate(count, (_) => acquire());
}
/// 批量归还
void releaseBatch(Iterable<PooledComponentData> 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<String, Queue<Paint>> _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<ID, Component> _loadedComponents = {};
final Set<ID> _pendingLoads = {};
final int _maxLoadedComponents;
final Queue<ID> _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<ID, Component> components) {
for (final entry in components.entries) {
set(entry.key, entry.value);
}
}
/// 卸载视口外的元件
void unloadOutsideViewport(ViewportConfig viewport) {
final toUnload = <ID>[];
_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<ID, Component> 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<String, CachedImage> _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 = <String>[];
_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<OptimizedEditableCanvas> createState() => _OptimizedEditableCanvasState();
}
class _OptimizedEditableCanvasState extends State<OptimizedEditableCanvas> {
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,
),
);
},
);
}
}
*/