-
Notifications
You must be signed in to change notification settings - Fork 52
fix lifecycle cleanup issues #222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,10 +13,13 @@ class SensorStreams { | |
| static final Map<Sensor, Stream<SensorValue>> _sharedStreams = {}; | ||
|
|
||
| static Stream<SensorValue> shared(Sensor sensor) { | ||
| return _sharedStreams.putIfAbsent( | ||
| sensor, | ||
| () => sensor.sensorStream.asBroadcastStream(), | ||
| ); | ||
| return _sharedStreams.putIfAbsent(sensor, () { | ||
| return sensor.sensorStream.asBroadcastStream( | ||
| onCancel: (_) { | ||
| _sharedStreams.remove(sensor); | ||
| }, | ||
| ); | ||
| }); | ||
|
Comment on lines
15
to
+22
|
||
| } | ||
|
|
||
| static void clearForSensor(Sensor sensor) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -107,6 +107,7 @@ class WearablesProvider with ChangeNotifier { | |
| final Map<Wearable, SensorConfigurationProvider> | ||
| _sensorConfigurationProviders = {}; | ||
| final Set<String> _splitStereoPairKeys = {}; | ||
| bool _disposed = false; | ||
|
|
||
| List<Wearable> get wearables => _wearables; | ||
| Map<Wearable, SensorConfigurationProvider> get sensorConfigurationProviders => | ||
|
|
@@ -161,9 +162,19 @@ class WearablesProvider with ChangeNotifier { | |
| _wearables.any((w) => w.deviceId == wearable.deviceId); | ||
|
|
||
| void _emitWearableEvent(WearableEvent event) { | ||
| if (_disposed || _wearableEventController.isClosed) { | ||
| return; | ||
| } | ||
| _wearableEventController.add(event); | ||
| } | ||
|
|
||
| void _emitUnsupportedFirmwareEvent(UnsupportedFirmwareEvent event) { | ||
| if (_disposed || _unsupportedFirmwareEventsController.isClosed) { | ||
| return; | ||
| } | ||
| _unsupportedFirmwareEventsController.add(event); | ||
| } | ||
|
|
||
| void _emitWearableError({ | ||
| required Wearable wearable, | ||
| required String errorMessage, | ||
|
|
@@ -180,6 +191,9 @@ class WearablesProvider with ChangeNotifier { | |
|
|
||
| void _scheduleMicrotask(FutureOr<void> Function() work) { | ||
| Future.microtask(() async { | ||
| if (_disposed) { | ||
| return; | ||
| } | ||
| try { | ||
| await work(); | ||
| } catch (e, st) { | ||
|
|
@@ -374,16 +388,14 @@ class WearablesProvider with ChangeNotifier { | |
| // All good, nothing to do. | ||
| break; | ||
| case FirmwareSupportStatus.tooNew: | ||
| _unsupportedFirmwareEventsController | ||
| .add(FirmwareTooNewEvent(wearable)); | ||
| _emitUnsupportedFirmwareEvent(FirmwareTooNewEvent(wearable)); | ||
| break; | ||
| case FirmwareSupportStatus.unsupported: | ||
| _unsupportedFirmwareEventsController | ||
| .add(FirmwareUnsupportedEvent(wearable)); | ||
| _emitUnsupportedFirmwareEvent(FirmwareUnsupportedEvent(wearable)); | ||
| break; | ||
| case FirmwareSupportStatus.tooOld: | ||
| _unsupportedFirmwareEventsController | ||
| .add(FirmwareTooOldEvent(wearable)); | ||
| _emitUnsupportedFirmwareEvent(FirmwareTooOldEvent(wearable)); | ||
DennisMoschina marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| break; | ||
| case FirmwareSupportStatus.unknown: | ||
| logger.w('Firmware support unknown for ${wearable.name}'); | ||
| break; | ||
|
|
@@ -419,7 +431,7 @@ class WearablesProvider with ChangeNotifier { | |
| logger.i( | ||
| 'Newer firmware available for ${(dev as Wearable).name}: $currentVersion -> $latestVersion', | ||
| ); | ||
| _wearableEventController.add( | ||
| _emitWearableEvent( | ||
| NewFirmwareAvailableEvent( | ||
| wearable: dev as Wearable, | ||
| currentVersion: currentVersion, | ||
|
|
@@ -483,9 +495,11 @@ class WearablesProvider with ChangeNotifier { | |
| ), | ||
| ); | ||
| _wearables.remove(wearable); | ||
| _sensorConfigurationProviders.remove(wearable); | ||
| _sensorConfigurationProviders.remove(wearable)?.dispose(); | ||
| _capabilitySubscriptions.remove(wearable)?.cancel(); | ||
| notifyListeners(); | ||
| if (!_disposed) { | ||
| notifyListeners(); | ||
| } | ||
| } | ||
|
|
||
| SensorConfigurationProvider getSensorConfigurationProvider( | ||
|
|
@@ -514,8 +528,31 @@ class WearablesProvider with ChangeNotifier { | |
| ), | ||
| ); | ||
| } | ||
| if (_disposed) { | ||
|
||
| return; | ||
| } | ||
| if (addedCapabilites.isNotEmpty) { | ||
| notifyListeners(); | ||
| } | ||
| } | ||
|
|
||
| @override | ||
| void dispose() { | ||
| _disposed = true; | ||
| for (final sub in _capabilitySubscriptions.values) { | ||
| unawaited(sub.cancel()); | ||
| } | ||
| _capabilitySubscriptions.clear(); | ||
|
|
||
| for (final provider in _sensorConfigurationProviders.values) { | ||
| provider.dispose(); | ||
| } | ||
| _sensorConfigurationProviders.clear(); | ||
| _wearables.clear(); | ||
| _splitStereoPairKeys.clear(); | ||
|
|
||
| unawaited(_unsupportedFirmwareEventsController.close()); | ||
| unawaited(_wearableEventController.close()); | ||
| super.dispose(); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SensorStreams.sharedonly evicts the cached entry viaonCancel. If the underlyingsensor.sensorStreamcompletes (e.g., because a device disconnects) without listeners explicitly canceling first,onCancelwon’t run and_sharedStreamscan retain a closed stream, causing future callers to receive an already-terminated stream. Consider wrapping the source stream so you also remove the cache entry ononDone/onError(or otherwise ensureclearForSensoris invoked on disconnect).