memory leak tracking & fixes
This commit is contained in:
parent
bca78a0669
commit
c5fde95c73
19 changed files with 118 additions and 40 deletions
|
@ -4,7 +4,8 @@ class AnalysisController {
|
||||||
final bool canStartService, force;
|
final bool canStartService, force;
|
||||||
final int progressTotal, progressOffset;
|
final int progressTotal, progressOffset;
|
||||||
final List<int>? entryIds;
|
final List<int>? entryIds;
|
||||||
final ValueNotifier<bool> stopSignal;
|
|
||||||
|
final ValueNotifier<bool> _stopSignal = ValueNotifier(false);
|
||||||
|
|
||||||
AnalysisController({
|
AnalysisController({
|
||||||
this.canStartService = true,
|
this.canStartService = true,
|
||||||
|
@ -12,8 +13,24 @@ class AnalysisController {
|
||||||
this.force = false,
|
this.force = false,
|
||||||
this.progressTotal = 0,
|
this.progressTotal = 0,
|
||||||
this.progressOffset = 0,
|
this.progressOffset = 0,
|
||||||
ValueNotifier<bool>? stopSignal,
|
}) {
|
||||||
}) : stopSignal = stopSignal ?? ValueNotifier(false);
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$AnalysisController',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool get isStopping => stopSignal.value;
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
|
_stopSignal.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isStopping => _stopSignal.value;
|
||||||
|
|
||||||
|
void enableStopSignal() => _stopSignal.value = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,13 @@ mixin SourceBase {
|
||||||
|
|
||||||
abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, PlaceMixin, StateMixin, LocationMixin, TagMixin, TrashMixin {
|
abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, PlaceMixin, StateMixin, LocationMixin, TagMixin, TrashMixin {
|
||||||
CollectionSource() {
|
CollectionSource() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$CollectionSource',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
settings.updateStream.where((event) => event.key == SettingKeys.localeKey).listen((_) => invalidateAlbumDisplayNames());
|
settings.updateStream.where((event) => event.key == SettingKeys.localeKey).listen((_) => invalidateAlbumDisplayNames());
|
||||||
settings.updateStream.where((event) => event.key == SettingKeys.hiddenFiltersKey).listen((event) {
|
settings.updateStream.where((event) => event.key == SettingKeys.hiddenFiltersKey).listen((event) {
|
||||||
final oldValue = event.oldValue;
|
final oldValue = event.oldValue;
|
||||||
|
@ -76,6 +83,14 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mustCallSuper
|
||||||
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
|
_rawEntries.forEach((v) => v.dispose());
|
||||||
|
}
|
||||||
|
|
||||||
final EventBus _eventBus = EventBus();
|
final EventBus _eventBus = EventBus();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -447,7 +462,8 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
||||||
|
|
||||||
Future<void> analyze(AnalysisController? analysisController, {Set<AvesEntry>? entries}) async {
|
Future<void> analyze(AnalysisController? analysisController, {Set<AvesEntry>? entries}) async {
|
||||||
final todoEntries = entries ?? visibleEntries;
|
final todoEntries = entries ?? visibleEntries;
|
||||||
final _analysisController = analysisController ?? AnalysisController();
|
final defaultAnalysisController = AnalysisController();
|
||||||
|
final _analysisController = analysisController ?? defaultAnalysisController;
|
||||||
final force = _analysisController.force;
|
final force = _analysisController.force;
|
||||||
if (!_analysisController.isStopping) {
|
if (!_analysisController.isStopping) {
|
||||||
var startAnalysisService = false;
|
var startAnalysisService = false;
|
||||||
|
@ -481,6 +497,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
||||||
updateDerivedFilters(todoEntries);
|
updateDerivedFilters(todoEntries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
defaultAnalysisController.dispose();
|
||||||
state = SourceState.ready;
|
state = SourceState.ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,9 +109,11 @@ class Analyzer {
|
||||||
if (kFlutterMemoryAllocationsEnabled) {
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
}
|
}
|
||||||
|
_stopUpdateTimer();
|
||||||
|
_controller?.dispose();
|
||||||
_serviceStateNotifier.removeListener(_onServiceStateChanged);
|
_serviceStateNotifier.removeListener(_onServiceStateChanged);
|
||||||
_source.stateNotifier.removeListener(_onSourceStateChanged);
|
_source.stateNotifier.removeListener(_onSourceStateChanged);
|
||||||
_stopUpdateTimer();
|
_source.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> start(dynamic args) async {
|
Future<void> start(dynamic args) async {
|
||||||
|
@ -125,13 +127,13 @@ class Analyzer {
|
||||||
progressOffset = args['progressOffset'];
|
progressOffset = args['progressOffset'];
|
||||||
}
|
}
|
||||||
debugPrint('$runtimeType start for ${entryIds?.length ?? 'all'} entries, at $progressOffset/$progressTotal');
|
debugPrint('$runtimeType start for ${entryIds?.length ?? 'all'} entries, at $progressOffset/$progressTotal');
|
||||||
|
_controller?.dispose();
|
||||||
_controller = AnalysisController(
|
_controller = AnalysisController(
|
||||||
canStartService: false,
|
canStartService: false,
|
||||||
entryIds: entryIds,
|
entryIds: entryIds,
|
||||||
force: force,
|
force: force,
|
||||||
progressTotal: progressTotal,
|
progressTotal: progressTotal,
|
||||||
progressOffset: progressOffset,
|
progressOffset: progressOffset,
|
||||||
stopSignal: ValueNotifier(false),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.systemLocalesFallback = await deviceService.getLocales();
|
settings.systemLocalesFallback = await deviceService.getLocales();
|
||||||
|
@ -160,7 +162,7 @@ class Analyzer {
|
||||||
await _stopPlatformService();
|
await _stopPlatformService();
|
||||||
_serviceStateNotifier.value = AnalyzerState.stopped;
|
_serviceStateNotifier.value = AnalyzerState.stopped;
|
||||||
case AnalyzerState.stopped:
|
case AnalyzerState.stopped:
|
||||||
_controller?.stopSignal.value = true;
|
_controller?.enableStopSignal();
|
||||||
_stopUpdateTimer();
|
_stopUpdateTimer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,5 +90,6 @@ Future<AvesEntry?> _getWidgetEntry(int widgetId, bool reuseEntry) async {
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
settings.setWidgetUri(widgetId, entry.uri);
|
settings.setWidgetUri(widgetId, entry.uri);
|
||||||
}
|
}
|
||||||
|
source.dispose();
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,13 +196,14 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_pageTransitionsBuilderNotifier.dispose();
|
|
||||||
_tvMediaQueryModifierNotifier.dispose();
|
|
||||||
_appModeNotifier.dispose();
|
|
||||||
_subscriptions
|
_subscriptions
|
||||||
..forEach((sub) => sub.cancel())
|
..forEach((sub) => sub.cancel())
|
||||||
..clear();
|
..clear();
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
_pageTransitionsBuilderNotifier.dispose();
|
||||||
|
_tvMediaQueryModifierNotifier.dispose();
|
||||||
|
_appModeNotifier.dispose();
|
||||||
|
_mediaStoreSource.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -257,7 +257,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
|
|
||||||
final controller = AnalysisController(canStartService: true, force: true);
|
final controller = AnalysisController(canStartService: true, force: true);
|
||||||
final collection = context.read<CollectionLens>();
|
final collection = context.read<CollectionLens>();
|
||||||
collection.source.analyze(controller, entries: entries);
|
collection.source.analyze(controller, entries: entries).then((_) => controller.dispose());
|
||||||
|
|
||||||
_browse(context);
|
_browse(context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,9 +89,11 @@ abstract class ChooserQuickButtonState<T extends ChooserQuickButton<U>, U> exten
|
||||||
}
|
}
|
||||||
|
|
||||||
void _clearChooserOverlayEntry() {
|
void _clearChooserOverlayEntry() {
|
||||||
if (_chooserOverlayEntry != null) {
|
final overlayEntry = _chooserOverlayEntry;
|
||||||
_chooserOverlayEntry!.remove();
|
_chooserOverlayEntry = null;
|
||||||
_chooserOverlayEntry = null;
|
if (overlayEntry != null) {
|
||||||
|
overlayEntry.remove();
|
||||||
|
overlayEntry.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,9 +180,12 @@ class _GridScaleGestureDetectorState<T> extends State<GridScaleGestureDetector<T
|
||||||
|
|
||||||
void _onScaleEnd(ScaleEndDetails details) {
|
void _onScaleEnd(ScaleEndDetails details) {
|
||||||
if (_scaledSizeNotifier == null) return;
|
if (_scaledSizeNotifier == null) return;
|
||||||
if (_overlayEntry != null) {
|
|
||||||
_overlayEntry!.remove();
|
final overlayEntry = _overlayEntry;
|
||||||
_overlayEntry = null;
|
_overlayEntry = null;
|
||||||
|
if (overlayEntry != null) {
|
||||||
|
overlayEntry.remove();
|
||||||
|
overlayEntry.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_applyingScale = true;
|
_applyingScale = true;
|
||||||
|
|
|
@ -38,6 +38,12 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
_startDbReport();
|
_startDbReport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_disposeLoadedContent();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
|
@ -63,7 +69,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => metadataDb.reset().then((_) => _startDbReport()),
|
onPressed: () => metadataDb.reset().then((_) => _reload()),
|
||||||
child: const Text('Reset'),
|
child: const Text('Reset'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -86,7 +92,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => metadataDb.clearEntries().then((_) => _startDbReport()),
|
onPressed: () => metadataDb.clearEntries().then((_) => _reload()),
|
||||||
child: const Text('Clear'),
|
child: const Text('Clear'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -107,7 +113,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => metadataDb.clearDates().then((_) => _startDbReport()),
|
onPressed: () => metadataDb.clearDates().then((_) => _reload()),
|
||||||
child: const Text('Clear'),
|
child: const Text('Clear'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -128,7 +134,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => metadataDb.clearCatalogMetadata().then((_) => _startDbReport()),
|
onPressed: () => metadataDb.clearCatalogMetadata().then((_) => _reload()),
|
||||||
child: const Text('Clear'),
|
child: const Text('Clear'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -149,7 +155,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => metadataDb.clearAddresses().then((_) => _startDbReport()),
|
onPressed: () => metadataDb.clearAddresses().then((_) => _reload()),
|
||||||
child: const Text('Clear'),
|
child: const Text('Clear'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -170,7 +176,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => metadataDb.clearTrashDetails().then((_) => _startDbReport()),
|
onPressed: () => metadataDb.clearTrashDetails().then((_) => _reload()),
|
||||||
child: const Text('Clear'),
|
child: const Text('Clear'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -191,7 +197,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => vaults.clear().then((_) => _startDbReport()),
|
onPressed: () => vaults.clear().then((_) => _reload()),
|
||||||
child: const Text('Clear'),
|
child: const Text('Clear'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -212,7 +218,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => favourites.clear().then((_) => _startDbReport()),
|
onPressed: () => favourites.clear().then((_) => _reload()),
|
||||||
child: const Text('Clear'),
|
child: const Text('Clear'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -233,7 +239,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => covers.clear().then((_) => _startDbReport()),
|
onPressed: () => covers.clear().then((_) => _reload()),
|
||||||
child: const Text('Clear'),
|
child: const Text('Clear'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -254,7 +260,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => metadataDb.clearVideoPlayback().then((_) => _startDbReport()),
|
onPressed: () => metadataDb.clearVideoPlayback().then((_) => _reload()),
|
||||||
child: const Text('Clear'),
|
child: const Text('Clear'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -268,6 +274,11 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _reload() async {
|
||||||
|
await _disposeLoadedContent();
|
||||||
|
_startDbReport();
|
||||||
|
}
|
||||||
|
|
||||||
void _startDbReport() {
|
void _startDbReport() {
|
||||||
_dbFileSizeLoader = metadataDb.dbFileSize();
|
_dbFileSizeLoader = metadataDb.dbFileSize();
|
||||||
_dbEntryLoader = metadataDb.loadEntries();
|
_dbEntryLoader = metadataDb.loadEntries();
|
||||||
|
@ -281,6 +292,10 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
||||||
_dbVideoPlaybackLoader = metadataDb.loadAllVideoPlayback();
|
_dbVideoPlaybackLoader = metadataDb.loadAllVideoPlayback();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _disposeLoadedContent() async {
|
||||||
|
(await _dbEntryLoader).forEach((v) => v.dispose());
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
bool get wantKeepAlive => true;
|
||||||
|
|
|
@ -47,14 +47,17 @@ class _DebugGeneralSectionState extends State<DebugGeneralSection> with Automati
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
value: _taskQueueOverlayEntry != null,
|
value: _taskQueueOverlayEntry != null,
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
_taskQueueOverlayEntry?.remove();
|
final overlayEntry = _taskQueueOverlayEntry;
|
||||||
|
_taskQueueOverlayEntry = null;
|
||||||
|
if (overlayEntry != null) {
|
||||||
|
overlayEntry.remove();
|
||||||
|
overlayEntry.dispose();
|
||||||
|
}
|
||||||
if (v) {
|
if (v) {
|
||||||
_taskQueueOverlayEntry = OverlayEntry(
|
_taskQueueOverlayEntry = OverlayEntry(
|
||||||
builder: (context) => const DebugTaskQueueOverlay(),
|
builder: (context) => const DebugTaskQueueOverlay(),
|
||||||
);
|
);
|
||||||
Overlay.of(context).insert(_taskQueueOverlayEntry!);
|
Overlay.of(context).insert(_taskQueueOverlayEntry!);
|
||||||
} else {
|
|
||||||
_taskQueueOverlayEntry = null;
|
|
||||||
}
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
|
|
|
@ -163,7 +163,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
||||||
_overlayAnimationController.dispose();
|
_overlayAnimationController.dispose();
|
||||||
_overlayVisible.removeListener(_onOverlayVisibleChanged);
|
_overlayVisible.removeListener(_onOverlayVisibleChanged);
|
||||||
_mapController.dispose();
|
_mapController.dispose();
|
||||||
_selectedIndexNotifier.removeListener(_onThumbnailIndexChanged);
|
_selectedIndexNotifier.dispose();
|
||||||
regionCollection?.dispose();
|
regionCollection?.dispose();
|
||||||
// provided collection should be a new instance specifically created
|
// provided collection should be a new instance specifically created
|
||||||
// for the `MapPage` widget, so it can be safely disposed here
|
// for the `MapPage` widget, so it can be safely disposed here
|
||||||
|
|
|
@ -49,7 +49,7 @@ class _ViewerThumbnailPreviewState extends State<ViewerThumbnailPreview> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_entryIndexNotifier.removeListener(_onScrollerIndexChanged);
|
_entryIndexNotifier.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,11 +33,10 @@ class VideoConductor {
|
||||||
if (kFlutterMemoryAllocationsEnabled) {
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
}
|
}
|
||||||
await _disposeAll();
|
|
||||||
_subscriptions
|
_subscriptions
|
||||||
..forEach((sub) => sub.cancel())
|
..forEach((sub) => sub.cancel())
|
||||||
..clear();
|
..clear();
|
||||||
_controllers.forEach((v) => v.dispose());
|
await _disposeAll();
|
||||||
_controllers.clear();
|
_controllers.clear();
|
||||||
if (settings.keepScreenOn == KeepScreenOn.videoPlayback) {
|
if (settings.keepScreenOn == KeepScreenOn.videoPlayback) {
|
||||||
await windowService.keepScreenOn(false);
|
await windowService.keepScreenOn(false);
|
||||||
|
|
|
@ -73,6 +73,10 @@ class ViewStateConductor {
|
||||||
entry,
|
entry,
|
||||||
...?entry.burstEntries,
|
...?entry.burstEntries,
|
||||||
}.map((v) => v.uri).toSet();
|
}.map((v) => v.uri).toSet();
|
||||||
_controllers.removeWhere((v) => uris.contains(v.entry.uri));
|
final entryControllers = _controllers.where((v) => uris.contains(v.entry.uri)).toSet();
|
||||||
|
entryControllers.forEach((controller) {
|
||||||
|
_controllers.remove(controller);
|
||||||
|
controller.dispose();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,5 +29,6 @@ class ViewStateController with HistogramMixin {
|
||||||
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
}
|
}
|
||||||
viewStateNotifier.dispose();
|
viewStateNotifier.dispose();
|
||||||
|
fullImageNotifier.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ class _EntryPageViewState extends State<EntryPageView> with SingleTickerProvider
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_unregisterWidget(widget);
|
_unregisterWidget(widget);
|
||||||
widget.onDisposed?.call();
|
widget.onDisposed?.call();
|
||||||
|
_actionFeedbackChildNotifier.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,9 +306,11 @@ class _EntryPageViewState extends State<EntryPageView> with SingleTickerProvider
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
onScaleEnd = (details) {
|
onScaleEnd = (details) {
|
||||||
if (_actionFeedbackOverlayEntry != null) {
|
final overlayEntry = _actionFeedbackOverlayEntry;
|
||||||
_actionFeedbackOverlayEntry!.remove();
|
_actionFeedbackOverlayEntry = null;
|
||||||
_actionFeedbackOverlayEntry = null;
|
if (overlayEntry != null) {
|
||||||
|
overlayEntry.remove();
|
||||||
|
overlayEntry.dispose();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,7 @@ class _VideoCoverState extends State<VideoCover> {
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_unregisterWidget(widget);
|
_unregisterWidget(widget);
|
||||||
|
_videoCoverInfoNotifier.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,12 @@ class MagnifierGestureDetector extends StatefulWidget {
|
||||||
class _MagnifierGestureDetectorState extends State<MagnifierGestureDetector> {
|
class _MagnifierGestureDetectorState extends State<MagnifierGestureDetector> {
|
||||||
final ValueNotifier<TapDownDetails?> doubleTapDetails = ValueNotifier(null);
|
final ValueNotifier<TapDownDetails?> doubleTapDetails = ValueNotifier(null);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
doubleTapDetails.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final gestureSettings = MediaQuery.gestureSettingsOf(context);
|
final gestureSettings = MediaQuery.gestureSettingsOf(context);
|
||||||
|
|
|
@ -12,7 +12,7 @@ import 'package:media_kit_video/media_kit_video.dart';
|
||||||
class MpvVideoController extends AvesVideoController {
|
class MpvVideoController extends AvesVideoController {
|
||||||
late Player _instance;
|
late Player _instance;
|
||||||
late VideoStatus _status;
|
late VideoStatus _status;
|
||||||
bool _firstFrameRendered = false;
|
bool _disposed = false, _firstFrameRendered = false;
|
||||||
final ValueNotifier<VideoController?> _controllerNotifier = ValueNotifier(null);
|
final ValueNotifier<VideoController?> _controllerNotifier = ValueNotifier(null);
|
||||||
final List<StreamSubscription> _subscriptions = [];
|
final List<StreamSubscription> _subscriptions = [];
|
||||||
final StreamController<VideoStatus> _statusStreamController = StreamController.broadcast();
|
final StreamController<VideoStatus> _statusStreamController = StreamController.broadcast();
|
||||||
|
@ -63,12 +63,15 @@ class MpvVideoController extends AvesVideoController {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
|
assert(!_disposed);
|
||||||
|
_disposed = true;
|
||||||
await super.dispose();
|
await super.dispose();
|
||||||
_stopListening();
|
_stopListening();
|
||||||
_stopStreamFetchTimer();
|
_stopStreamFetchTimer();
|
||||||
await _statusStreamController.close();
|
await _statusStreamController.close();
|
||||||
await _timedTextStreamController.close();
|
await _timedTextStreamController.close();
|
||||||
await _instance.dispose();
|
await _instance.dispose();
|
||||||
|
_controllerNotifier.dispose();
|
||||||
_completedNotifier.dispose();
|
_completedNotifier.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue