13
13
import android .view .Surface ;
14
14
import androidx .annotation .NonNull ;
15
15
import androidx .annotation .Nullable ;
16
+ import androidx .annotation .VisibleForTesting ;
16
17
import io .flutter .Log ;
17
18
import io .flutter .embedding .engine .FlutterJNI ;
18
19
import io .flutter .view .TextureRegistry ;
20
+ import java .lang .ref .WeakReference ;
19
21
import java .nio .ByteBuffer ;
20
22
import java .util .ArrayList ;
23
+ import java .util .HashSet ;
24
+ import java .util .Iterator ;
21
25
import java .util .List ;
22
26
import java .util .Set ;
23
- import java .util .concurrent .CopyOnWriteArraySet ;
24
27
import java .util .concurrent .atomic .AtomicLong ;
25
28
26
29
/**
@@ -47,8 +50,8 @@ public class FlutterRenderer implements TextureRegistry {
47
50
private Handler handler = new Handler ();
48
51
49
52
@ NonNull
50
- private final Set <TextureRegistry .OnLowMemoryListener > onLowMemoryListeners =
51
- new CopyOnWriteArraySet <>();
53
+ private final Set <WeakReference < TextureRegistry .OnTrimMemoryListener >> onTrimMemoryListeners =
54
+ new HashSet <>();
52
55
53
56
@ NonNull
54
57
private final FlutterUiDisplayListener flutterUiDisplayListener =
@@ -98,16 +101,25 @@ public void removeIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListene
98
101
}
99
102
100
103
/** Adds a listener that is invoked when a memory pressure warning was forward. */
101
- public void addOnLowMemoryListener (@ NonNull OnLowMemoryListener listener ) {
102
- onLowMemoryListeners .add (listener );
104
+ @ VisibleForTesting
105
+ /* package */ void addOnTrimMemoryListener (@ NonNull OnTrimMemoryListener listener ) {
106
+ onTrimMemoryListeners .add (new WeakReference <>(listener ));
103
107
}
104
108
105
109
/**
106
- * Removes a {@link OnLowMemoryListener } that was added with {@link
107
- * #addOnLowMemoryListener(OnLowMemoryListener )}.
110
+ * Removes a {@link OnTrimMemoryListener } that was added with {@link
111
+ * #addOnTrimMemoryListener(OnTrimMemoryListener )}.
108
112
*/
109
- public void removeOnLowMemoryListener (@ NonNull OnLowMemoryListener listener ) {
110
- onLowMemoryListeners .remove (listener );
113
+ @ VisibleForTesting
114
+ /* package */ void removeOnTrimMemoryListener (@ NonNull OnTrimMemoryListener listener ) {
115
+ final Iterator <WeakReference <OnTrimMemoryListener >> iterator = onTrimMemoryListeners .iterator ();
116
+ while (iterator .hasNext ()) {
117
+ final WeakReference <OnTrimMemoryListener > listenerRef = iterator .next ();
118
+ if (listenerRef .get () == listener ) {
119
+ iterator .remove ();
120
+ break ;
121
+ }
122
+ }
111
123
}
112
124
113
125
// ------ START TextureRegistry IMPLEMENTATION -----
@@ -133,23 +145,34 @@ public SurfaceTextureEntry registerSurfaceTexture(@NonNull SurfaceTexture surfac
133
145
new SurfaceTextureRegistryEntry (nextTextureId .getAndIncrement (), surfaceTexture );
134
146
Log .v (TAG , "New SurfaceTexture ID: " + entry .id ());
135
147
registerTexture (entry .id (), entry .textureWrapper ());
136
- addOnLowMemoryListener (entry );
148
+ addOnTrimMemoryListener (entry );
137
149
return entry ;
138
150
}
139
151
140
152
@ Override
141
153
public void onTrimMemory (int level ) {
142
- for (TextureRegistry .OnLowMemoryListener listener : onLowMemoryListeners ) {
143
- listener .onLowMemory (level );
154
+ Set <WeakReference <OnTrimMemoryListener >> listenerToBeDeleted = new HashSet <>();
155
+ for (WeakReference <OnTrimMemoryListener > listenerRef : onTrimMemoryListeners ) {
156
+ final OnTrimMemoryListener listener = listenerRef .get ();
157
+ if (listener != null ) {
158
+ listener .onTrimMemory (level );
159
+ } else {
160
+ listenerToBeDeleted .add (listenerRef );
161
+ }
162
+ }
163
+
164
+ // Purge cleared refs lazily to avoid accumulating a lot of dead listener.
165
+ for (WeakReference <OnTrimMemoryListener > listenerRef : listenerToBeDeleted ) {
166
+ onTrimMemoryListeners .remove (listenerRef );
144
167
}
145
168
}
146
169
147
170
final class SurfaceTextureRegistryEntry
148
- implements TextureRegistry .SurfaceTextureEntry , TextureRegistry .OnLowMemoryListener {
171
+ implements TextureRegistry .SurfaceTextureEntry , TextureRegistry .OnTrimMemoryListener {
149
172
private final long id ;
150
173
@ NonNull private final SurfaceTextureWrapper textureWrapper ;
151
174
private boolean released ;
152
- @ Nullable private OnLowMemoryListener lowMemoryListener ;
175
+ @ Nullable private OnTrimMemoryListener trimMemoryListener ;
153
176
@ Nullable private OnFrameConsumedListener frameConsumedListener ;
154
177
private final Runnable onFrameConsumed =
155
178
new Runnable () {
@@ -181,9 +204,9 @@ public void run() {
181
204
}
182
205
183
206
@ Override
184
- public void onLowMemory (int level ) {
185
- if (lowMemoryListener != null ) {
186
- lowMemoryListener . onLowMemory (level );
207
+ public void onTrimMemory (int level ) {
208
+ if (trimMemoryListener != null ) {
209
+ trimMemoryListener . onTrimMemory (level );
187
210
}
188
211
}
189
212
@@ -203,7 +226,7 @@ public void onFrameAvailable(@NonNull SurfaceTexture texture) {
203
226
};
204
227
205
228
private void removeListener () {
206
- removeOnLowMemoryListener (this );
229
+ removeOnTrimMemoryListener (this );
207
230
}
208
231
209
232
@ NonNull
@@ -241,17 +264,7 @@ protected void finalize() throws Throwable {
241
264
return ;
242
265
}
243
266
244
- handler .post (
245
- new Runnable () {
246
- @ Override
247
- public void run () {
248
- if (!flutterJNI .isAttached ()) {
249
- return ;
250
- }
251
- unregisterTexture (id );
252
- removeListener ();
253
- }
254
- });
267
+ handler .post (new SurfaceTextureFinalizerRunnable (id , flutterJNI ));
255
268
} finally {
256
269
super .finalize ();
257
270
}
@@ -263,8 +276,27 @@ public void setOnFrameConsumedListener(@Nullable OnFrameConsumedListener listene
263
276
}
264
277
265
278
@ Override
266
- public void setOnLowMemoryListener (@ Nullable OnLowMemoryListener listener ) {
267
- lowMemoryListener = listener ;
279
+ public void setOnTrimMemoryListener (@ Nullable OnTrimMemoryListener listener ) {
280
+ trimMemoryListener = listener ;
281
+ }
282
+ }
283
+
284
+ static final class SurfaceTextureFinalizerRunnable implements Runnable {
285
+ private final long id ;
286
+ private final FlutterJNI flutterJNI ;
287
+
288
+ SurfaceTextureFinalizerRunnable (long id , @ NonNull FlutterJNI flutterJNI ) {
289
+ this .id = id ;
290
+ this .flutterJNI = flutterJNI ;
291
+ }
292
+
293
+ @ Override
294
+ public void run () {
295
+ if (!flutterJNI .isAttached ()) {
296
+ return ;
297
+ }
298
+ Log .v (TAG , "Releasing a SurfaceTexture (" + id + ")." );
299
+ flutterJNI .unregisterTexture (id );
268
300
}
269
301
}
270
302
// ------ END TextureRegistry IMPLEMENTATION ----
0 commit comments