@@ -38,7 +38,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
38
38
);
39
39
});
40
40
if (chunkEvents != null ) {
41
- chunkEvents.listen (
41
+ _chunkSubscription = chunkEvents.listen (
42
42
reportImageChunkEvent,
43
43
onError: (dynamic error, StackTrace stack) {
44
44
reportError (
@@ -65,10 +65,17 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
65
65
// How many frames have been emitted so far.
66
66
int _framesEmitted = 0 ;
67
67
Timer ? _timer;
68
+ StreamSubscription <ImageChunkEvent >? _chunkSubscription;
68
69
69
70
// Used to guard against registering multiple _handleAppFrame callbacks for the same frame.
70
71
bool _frameCallbackScheduled = false ;
71
72
73
+ /// We must avoid disposing a completer if it never had a listener, even
74
+ /// if all [keepAlive] handles get disposed.
75
+ bool __hadAtLeastOneListener = false ;
76
+
77
+ bool __disposed = false ;
78
+
72
79
void _switchToNewCodec () {
73
80
_framesEmitted = 0 ;
74
81
_timer = null ;
@@ -159,6 +166,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
159
166
160
167
@override
161
168
void addListener (ImageStreamListener listener) {
169
+ __hadAtLeastOneListener = true ;
162
170
if (! hasListeners && _codec != null ) _decodeNextFrameAndSchedule ();
163
171
super .addListener (listener);
164
172
}
@@ -169,6 +177,56 @@ class MultiImageStreamCompleter extends ImageStreamCompleter {
169
177
if (! hasListeners) {
170
178
_timer? .cancel ();
171
179
_timer = null ;
180
+ __maybeDispose ();
172
181
}
173
182
}
183
+
184
+ int __keepAliveHandles = 0 ;
185
+
186
+ @override
187
+ ImageStreamCompleterHandle keepAlive () {
188
+ final delegateHandle = super .keepAlive ();
189
+ return _MultiImageStreamCompleterHandle (this , delegateHandle);
190
+ }
191
+
192
+ void __maybeDispose () {
193
+ if (! __hadAtLeastOneListener ||
194
+ __disposed ||
195
+ hasListeners ||
196
+ __keepAliveHandles != 0 ) {
197
+ return ;
198
+ }
199
+
200
+ __disposed = true ;
201
+
202
+ _chunkSubscription? .onData (null );
203
+ _chunkSubscription? .cancel ();
204
+ _chunkSubscription = null ;
205
+ }
206
+ }
207
+
208
+ class _MultiImageStreamCompleterHandle implements ImageStreamCompleterHandle {
209
+ _MultiImageStreamCompleterHandle (this ._completer, this ._delegateHandle) {
210
+ _completer! .__keepAliveHandles += 1 ;
211
+ }
212
+
213
+ MultiImageStreamCompleter ? _completer;
214
+ final ImageStreamCompleterHandle _delegateHandle;
215
+
216
+ /// Call this method to signal the [ImageStreamCompleter] that it can now be
217
+ /// disposed when its last listener drops.
218
+ ///
219
+ /// This method must only be called once per object.
220
+ @override
221
+ void dispose () {
222
+ assert (_completer != null );
223
+ assert (_completer! .__keepAliveHandles > 0 );
224
+ assert (! _completer! .__disposed);
225
+
226
+ _delegateHandle.dispose ();
227
+
228
+ _completer! .__keepAliveHandles -= 1 ;
229
+ _completer! .__maybeDispose ();
230
+ _completer = null ;
231
+ }
174
232
}
0 commit comments