perf(disk): byte-cap the cover disk cache with an init sweep

This commit is contained in:
zarzet
2026-07-14 09:10:03 +07:00
parent 937ff6c4a2
commit 28cdb6c837
+44
View File
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
@@ -14,6 +15,11 @@ class CoverCacheManager {
static const String _cacheKey = 'coverImageCache';
static const int _maxCacheObjects = 1000;
static const Duration _maxCacheAge = Duration(days: 365);
// flutter_cache_manager only caps object count, not bytes; hi-res covers
// run 300KB-1.5MB each, so 1000 objects can mean hundreds of MB on a
// storage-starved device. Sweep oldest files past the byte cap on init.
static const int _maxCacheBytes = 150 << 20;
static const int _sweepTargetBytes = 120 << 20;
static CacheManager? _instance;
static bool _initialized = false;
@@ -44,11 +50,49 @@ class CoverCacheManager {
_initialized = true;
debugPrint('CoverCacheManager: Initialized successfully');
unawaited(_sweepOverByteCap(_cachePath!));
} catch (e) {
debugPrint('CoverCacheManager: Failed to initialize: $e');
}
}
/// Deletes oldest cover files until the cache is back under
/// [_sweepTargetBytes]. Stale JSON repo entries self-heal: a missing file
/// is a cache miss and gets re-downloaded on demand.
static Future<void> _sweepOverByteCap(String cachePath) async {
try {
final dir = Directory(cachePath);
if (!await dir.exists()) return;
final files = <File>[];
var totalSize = 0;
await for (final entity in dir.list(recursive: true)) {
if (entity is File && !entity.path.endsWith('.json')) {
files.add(entity);
totalSize += await entity.length();
}
}
if (totalSize <= _maxCacheBytes) return;
final stats = <File, FileStat>{
for (final file in files) file: await file.stat(),
};
files.sort(
(a, b) => stats[a]!.modified.compareTo(stats[b]!.modified),
);
for (final file in files) {
if (totalSize <= _sweepTargetBytes) break;
try {
totalSize -= stats[file]!.size;
await file.delete();
} catch (_) {}
}
debugPrint('CoverCacheManager: Swept cover cache over byte cap');
} catch (e) {
debugPrint('CoverCacheManager: Byte-cap sweep failed: $e');
}
}
static Future<void> clearCache() async {
if (!_initialized || _instance == null || _cachePath == null) {
await initialize();