mirror of
https://github.com/ZhuJHua/moodiary.git
synced 2026-04-05 16:31:45 +08:00
feat(cache): implement LRUCache and AsyncLRUCache for efficient data management
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moodiary/components/base/marquee.dart';
|
||||
import 'package:moodiary/utils/lru.dart';
|
||||
|
||||
class AdaptiveText extends StatelessWidget {
|
||||
final String text;
|
||||
@@ -82,6 +83,8 @@ class EllipsisText extends StatelessWidget {
|
||||
final String ellipsis;
|
||||
final int? maxLines;
|
||||
|
||||
static final _cache = LRUCache<String, String>(maxSize: 1000);
|
||||
|
||||
const EllipsisText(
|
||||
this.text, {
|
||||
super.key,
|
||||
@@ -90,10 +93,7 @@ class EllipsisText extends StatelessWidget {
|
||||
this.maxLines,
|
||||
});
|
||||
|
||||
double _calculateTextWidth(
|
||||
String text,
|
||||
TextScaler textScaler,
|
||||
) {
|
||||
double _calculateTextWidth(String text, TextScaler textScaler) {
|
||||
final span = TextSpan(text: text.fixAutoLines(), style: style);
|
||||
final tp = TextPainter(
|
||||
text: span,
|
||||
@@ -120,15 +120,31 @@ class EllipsisText extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textScaler = MediaQuery.textScalerOf(context);
|
||||
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
if (text.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final cacheKey =
|
||||
'${text.hashCode}_${ellipsis}_${constraints.maxWidth}_$maxLines';
|
||||
final cachedResult = _cache.get(cacheKey);
|
||||
if (cachedResult != null) {
|
||||
return Text(
|
||||
cachedResult,
|
||||
maxLines: maxLines,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: style,
|
||||
textScaler: textScaler,
|
||||
);
|
||||
}
|
||||
|
||||
if (!_createTextPainter(
|
||||
text,
|
||||
constraints.maxWidth,
|
||||
textScaler,
|
||||
).didExceedMaxLines) {
|
||||
_cache.put(cacheKey, text.fixAutoLines());
|
||||
return Text(
|
||||
text.fixAutoLines(),
|
||||
maxLines: maxLines,
|
||||
@@ -143,8 +159,6 @@ class EllipsisText extends StatelessWidget {
|
||||
double leftWidth = 0;
|
||||
double rightWidth = 0;
|
||||
|
||||
String truncatedText = text;
|
||||
|
||||
int lastValidLeftIndex = 0;
|
||||
int lastValidRightIndex = text.characters.length;
|
||||
|
||||
@@ -157,6 +171,7 @@ class EllipsisText extends StatelessWidget {
|
||||
rightWidth;
|
||||
final currentText =
|
||||
'${text.runeSubstring(0, leftIndex)}$ellipsis${text.runeSubstring(rightIndex)}';
|
||||
|
||||
if (_createTextPainter(
|
||||
currentText,
|
||||
constraints.maxWidth,
|
||||
@@ -176,10 +191,10 @@ class EllipsisText extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
final leftText = text.runeSubstring(0, lastValidLeftIndex);
|
||||
final rightText = text.runeSubstring(lastValidRightIndex);
|
||||
final truncatedText =
|
||||
'${text.runeSubstring(0, lastValidLeftIndex)}$ellipsis${text.runeSubstring(lastValidRightIndex)}';
|
||||
|
||||
truncatedText = '$leftText$ellipsis$rightText';
|
||||
_cache.put(cacheKey, truncatedText.fixAutoLines());
|
||||
|
||||
return Text(
|
||||
truncatedText.fixAutoLines(),
|
||||
|
||||
72
lib/utils/lru.dart
Normal file
72
lib/utils/lru.dart
Normal file
@@ -0,0 +1,72 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:synchronized/synchronized.dart';
|
||||
|
||||
class LRUCache<K, V> {
|
||||
final int maxSize;
|
||||
final LinkedHashMap<K, V> _map;
|
||||
|
||||
LRUCache({this.maxSize = 1000}) : _map = LinkedHashMap<K, V>();
|
||||
|
||||
V? get(K key) {
|
||||
if (!_map.containsKey(key)) return null;
|
||||
final value = _map.remove(key) as V;
|
||||
_map[key] = value;
|
||||
return value;
|
||||
}
|
||||
|
||||
void put(K key, V value) {
|
||||
if (_map.containsKey(key)) {
|
||||
_map.remove(key);
|
||||
} else if (_map.length >= maxSize) {
|
||||
_map.remove(_map.keys.first);
|
||||
}
|
||||
_map[key] = value;
|
||||
}
|
||||
|
||||
void clear() => _map.clear();
|
||||
|
||||
int size() => _map.length;
|
||||
|
||||
@override
|
||||
String toString() => _map.toString();
|
||||
}
|
||||
|
||||
class AsyncLRUCache<K, V> {
|
||||
final int maxSize;
|
||||
final LinkedHashMap<K, V> _map = LinkedHashMap<K, V>();
|
||||
final Lock _lock = Lock();
|
||||
|
||||
AsyncLRUCache({this.maxSize = 1000});
|
||||
|
||||
Future<V?> get(K key) async {
|
||||
return await _lock.synchronized(() async {
|
||||
if (!_map.containsKey(key)) return null;
|
||||
final value = _map.remove(key) as V;
|
||||
_map[key] = value;
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> put(K key, V value) async {
|
||||
await _lock.synchronized(() async {
|
||||
if (_map.containsKey(key)) {
|
||||
_map.remove(key);
|
||||
} else if (_map.length >= maxSize) {
|
||||
_map.remove(_map.keys.first);
|
||||
}
|
||||
_map[key] = value;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
await _lock.synchronized(() => _map.clear());
|
||||
}
|
||||
|
||||
Future<int> size() async {
|
||||
return await _lock.synchronized(() => _map.length);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => _map.toString();
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fc_native_video_thumbnail/fc_native_video_thumbnail.dart';
|
||||
@@ -16,6 +15,7 @@ import 'package:moodiary/src/rust/api/compress.dart';
|
||||
import 'package:moodiary/src/rust/api/constants.dart' as r_type;
|
||||
import 'package:moodiary/utils/file_util.dart';
|
||||
import 'package:moodiary/utils/log_util.dart';
|
||||
import 'package:moodiary/utils/lru.dart';
|
||||
import 'package:moodiary/utils/notice_util.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
@@ -25,6 +25,9 @@ class MediaUtil {
|
||||
|
||||
static final _thumbnail = FcNativeVideoThumbnail();
|
||||
|
||||
static final _imageAspectRatioCache =
|
||||
AsyncLRUCache<String, double>(maxSize: 1000);
|
||||
|
||||
static void useAndroidImagePicker() {
|
||||
final ImagePickerPlatform imagePickerImplementation =
|
||||
ImagePickerPlatform.instance;
|
||||
@@ -186,36 +189,25 @@ class MediaUtil {
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
static const int _maxCacheSize = 1000;
|
||||
static final LinkedHashMap<String, double> _cache = LinkedHashMap();
|
||||
|
||||
static Future<double> getImageAspectRatio(ImageProvider imageProvider) async {
|
||||
final key = _getImageKey(imageProvider);
|
||||
|
||||
if (_cache.containsKey(key)) {
|
||||
return _cache[key]!;
|
||||
final cachedAspectRatio = await _imageAspectRatioCache.get(key);
|
||||
if (cachedAspectRatio != null) {
|
||||
return cachedAspectRatio;
|
||||
}
|
||||
|
||||
final completer = Completer<double>();
|
||||
final imageStream = imageProvider.resolve(const ImageConfiguration());
|
||||
imageStream.addListener(
|
||||
ImageStreamListener((ImageInfo info, bool _) {
|
||||
ImageStreamListener((ImageInfo info, bool _) async {
|
||||
final aspectRatio = info.image.width / info.image.height;
|
||||
_addToCache(key, aspectRatio);
|
||||
await _imageAspectRatioCache.put(key, aspectRatio);
|
||||
completer.complete(aspectRatio);
|
||||
}),
|
||||
);
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
static void _addToCache(String key, double ratio) {
|
||||
if (_cache.length >= _maxCacheSize) {
|
||||
_cache.remove(_cache.keys.first);
|
||||
}
|
||||
_cache[key] = ratio;
|
||||
}
|
||||
|
||||
static String _getImageKey(ImageProvider imageProvider) {
|
||||
if (imageProvider is AssetImage) {
|
||||
return 'asset_${imageProvider.assetName}';
|
||||
|
||||
66
pubspec.lock
66
pubspec.lock
@@ -478,14 +478,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
csv:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csv
|
||||
sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1330,14 +1322,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
json2yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json2yaml
|
||||
sha256: da94630fbc56079426fdd167ae58373286f603371075b69bf46d848d63ba3e51
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1709,18 +1693,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: b15fad91c4d3d1f2b48c053dd41cb82da007c27407dc9ab5f9aa59881d0e39d4
|
||||
sha256: c447a3c3e7be4addf129b8f9ab6a4bd5d166b78918223e223b61fddf4d07e254
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.4"
|
||||
version: "8.2.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: a5ef9986efc7bf772f2696183a3992615baa76c1ffb1189318dd8803778fb05b
|
||||
sha256: "205ec83335c2ab9107bbba3f8997f9356d72ca3c715d2f038fc773d0366b4c76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.1.0"
|
||||
path:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -2186,22 +2170,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.8"
|
||||
sensors_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sensors_plus
|
||||
sha256: "905282c917c6bb731c242f928665c2ea15445aa491249dea9d98d7c79dc8fd39"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
sensors_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sensors_plus_platform_interface
|
||||
sha256: "58815d2f5e46c0c41c40fb39375d3f127306f7742efe3b891c0b1c87e2b5cd5d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -2311,30 +2279,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
slang:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: slang
|
||||
sha256: "2778b88f05ffc23fd0a37436f607bf2a541d0b6b922e69a8ea5bbd50c2427d18"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.1"
|
||||
slang_build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: slang_build_runner
|
||||
sha256: "15b251e8aa591d96a16da3678abdca814a5d21486d14f98602c52b169f794d7b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.2"
|
||||
slang_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: slang_flutter
|
||||
sha256: "819637a23348adbc4f4e8faee3f274d8908f9af31d57bf1e277cd730b14bacde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
sliver_tools:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -2488,7 +2432,7 @@ packages:
|
||||
source: hosted
|
||||
version: "28.2.3"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
|
||||
|
||||
@@ -27,7 +27,7 @@ dependencies:
|
||||
image_picker: 1.1.2
|
||||
device_info_plus: 11.2.2
|
||||
photo_view: 0.15.0
|
||||
package_info_plus: 8.1.4
|
||||
package_info_plus: 8.2.0
|
||||
uuid: 4.5.1
|
||||
flutter_quill: 11.0.0-dev.21
|
||||
share_plus: 10.1.4
|
||||
@@ -101,16 +101,14 @@ dependencies:
|
||||
sdk: flutter
|
||||
moodiary_rust:
|
||||
path: rust_builder
|
||||
slang: 4.4.1
|
||||
slang_flutter: 4.4.0
|
||||
modal_bottom_sheet: 3.0.0
|
||||
sheet: 1.0.0
|
||||
tutorial_coach_mark: 1.2.12
|
||||
adaptive_dialog: 2.4.0
|
||||
flutter_inappwebview: 6.1.5
|
||||
sensors_plus: 6.1.1
|
||||
markdown: 7.3.0
|
||||
flutter_highlight: 0.7.0
|
||||
synchronized: 3.3.0+3
|
||||
# //llama_cpp_dart: 0.0.8
|
||||
|
||||
|
||||
@@ -119,7 +117,6 @@ dev_dependencies:
|
||||
# sdk: flutter
|
||||
build_runner: 2.4.14
|
||||
flutter_launcher_icons: 0.14.3
|
||||
slang_build_runner: 4.4.2
|
||||
msix: 3.16.8
|
||||
flutter_lints: 5.0.0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user