perf: lazy extension VM init, incremental startup maintenance, and UI optimizations

- Defer extension VM initialization until first use with lockReadyVM() pattern to eliminate TOCTOU races and reduce startup overhead
- Add validateExtensionLoad() to catch JS errors at install time without keeping VM alive
- Teardown VM on extension disable to free resources; re-init lazily on re-enable
- Replace full orphan cleanup with incremental cursor-based pagination across launches
- Batch DB writes (upsertBatch, replaceAll) with transactions for atomicity
- Parse JSON natively on Kotlin side to avoid double-serialization over MethodChannel
- Add identity-based memoization caches for unified items and path match keys in queue tab
- Use ValueListenableBuilder for targeted embedded cover refreshes instead of full setState
- Extract shared widgets (_buildAlbumGridItemCore, _buildFilterButton, _navigateWithUnfocus)
- Use libraryCollectionsProvider selector and MediaQuery.paddingOf for fewer rebuilds
- Simplify supporter chip tiers and localize remaining hardcoded strings
This commit is contained in:
zarzet
2026-03-25 19:55:02 +07:00
parent da9d64ccfd
commit 03fd734048
18 changed files with 1279 additions and 1133 deletions
+14 -1
View File
@@ -22,6 +22,9 @@ const _audioExtensions = <String>[
'.aac',
];
const _maxPathMatchKeyCacheSize = 6000;
final Map<String, Set<String>> _pathMatchKeyCache = <String, Set<String>>{};
/// Strips a trailing audio extension from [path] if present.
/// Returns the path without extension, or `null` if no known audio extension
/// was found.
@@ -41,6 +44,11 @@ Set<String> buildPathMatchKeys(String? filePath) {
final cleaned = raw.startsWith('EXISTS:') ? raw.substring(7).trim() : raw;
if (cleaned.isEmpty) return const {};
final cached = _pathMatchKeyCache.remove(cleaned);
if (cached != null) {
_pathMatchKeyCache[cleaned] = cached;
return cached;
}
final keys = <String>{};
final visited = <String>{};
@@ -118,7 +126,12 @@ Set<String> buildPathMatchKeys(String? filePath) {
}
keys.addAll(extensionStrippedKeys);
return keys;
final result = Set<String>.unmodifiable(keys);
_pathMatchKeyCache[cleaned] = result;
while (_pathMatchKeyCache.length > _maxPathMatchKeyCacheSize) {
_pathMatchKeyCache.remove(_pathMatchKeyCache.keys.first);
}
return result;
}
Iterable<String> _androidEquivalentPaths(String path) {