Clear ImageCache on MemoryPressure (#53959)
diff --git a/packages/flutter/lib/src/painting/binding.dart b/packages/flutter/lib/src/painting/binding.dart
index d5f00a9..900d68e 100644
--- a/packages/flutter/lib/src/painting/binding.dart
+++ b/packages/flutter/lib/src/painting/binding.dart
@@ -99,6 +99,12 @@
imageCache.clearLiveImages();
}
+ @override
+ void handleMemoryPressure() {
+ super.handleMemoryPressure();
+ imageCache.clear();
+ }
+
/// Listenable that notifies when the available fonts on the system have
/// changed.
///
diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart
index 227b907..0c4ceed 100644
--- a/packages/flutter/lib/src/services/binding.dart
+++ b/packages/flutter/lib/src/services/binding.dart
@@ -48,13 +48,32 @@
return const _DefaultBinaryMessenger._();
}
+
+ /// Called when the operating system notifies the application of a memory
+ /// pressure situation.
+ ///
+ /// This method exposes the `memoryPressure` notification from
+ /// [SystemChannels.system].
+ @protected
+ @mustCallSuper
+ void handleMemoryPressure() { }
+
/// Handler called for messages received on the [SystemChannels.system]
/// message channel.
///
/// Other bindings may override this to respond to incoming system messages.
@protected
@mustCallSuper
- Future<void> handleSystemMessage(Object systemMessage) async { }
+ Future<void> handleSystemMessage(Object systemMessage) async {
+ final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
+ final String type = message['type'] as String;
+ switch (type) {
+ case 'memoryPressure':
+ handleMemoryPressure();
+ break;
+ }
+ return;
+ }
/// Adds relevant licenses to the [LicenseRegistry].
///
diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart
index d511001..a8f0183 100644
--- a/packages/flutter/lib/src/widgets/binding.dart
+++ b/packages/flutter/lib/src/widgets/binding.dart
@@ -655,32 +655,13 @@
observer.didChangeAppLifecycleState(state);
}
- /// Called when the operating system notifies the application of a memory
- /// pressure situation.
- ///
- /// Notifies all the observers using
- /// [WidgetsBindingObserver.didHaveMemoryPressure].
- ///
- /// This method exposes the `memoryPressure` notification from
- /// [SystemChannels.system].
+ @override
void handleMemoryPressure() {
+ super.handleMemoryPressure();
for (final WidgetsBindingObserver observer in _observers)
observer.didHaveMemoryPressure();
}
- @override
- Future<void> handleSystemMessage(Object systemMessage) async {
- await super.handleSystemMessage(systemMessage);
- final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
- final String type = message['type'] as String;
- switch (type) {
- case 'memoryPressure':
- handleMemoryPressure();
- break;
- }
- return;
- }
-
bool _needToReportFirstFrame = true;
final Completer<void> _firstFrameCompleter = Completer<void>();
diff --git a/packages/flutter/test/painting/binding_test.dart b/packages/flutter/test/painting/binding_test.dart
index f216b1a..74b5515 100644
--- a/packages/flutter/test/painting/binding_test.dart
+++ b/packages/flutter/test/painting/binding_test.dart
@@ -2,8 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:typed_data' show Uint8List;
-import 'dart:ui';
+import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@@ -11,21 +10,22 @@
import 'package:flutter/widgets.dart';
import 'package:flutter/painting.dart';
-import 'image_data.dart';
-import 'painting_utils.dart';
void main() {
- final PaintingBindingSpy binding = PaintingBindingSpy();
+ testWidgets('didHaveMemoryPressure clears imageCache', (WidgetTester tester) async {
+ imageCache.putIfAbsent(1, () => OneFrameImageStreamCompleter(
+ Future<ImageInfo>.value(ImageInfo(
+ image: FakeImage(),
+ scale: 1.0,
+ ),
+ )));
- test('instantiateImageCodec used for loading images', () async {
- expect(binding.instantiateImageCodecCalledCount, 0);
-
- final Uint8List bytes = Uint8List.fromList(kTransparentImage);
- final MemoryImage memoryImage = MemoryImage(bytes);
- memoryImage.load(memoryImage, (Uint8List bytes, {int cacheWidth, int cacheHeight}) {
- return PaintingBinding.instance.instantiateImageCodec(bytes, cacheWidth: cacheWidth, cacheHeight: cacheHeight);
- });
- expect(binding.instantiateImageCodecCalledCount, 1);
+ await tester.idle();
+ expect(imageCache.currentSize, 1);
+ final ByteData message = const JSONMessageCodec().encodeMessage(
+ <String, dynamic>{'type': 'memoryPressure'});
+ await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/system', message, (_) { });
+ expect(imageCache.currentSize, 0);
});
test('evict clears live references', () async {
@@ -84,7 +84,7 @@
void unlocked() {}
@override
- Window get window => throw UnimplementedError();
+ ui.Window get window => throw UnimplementedError();
}
class TestPaintingBinding extends TestBindingBase with ServicesBinding, PaintingBinding {
@@ -111,4 +111,20 @@
liveClearCount += 1;
super.clearLiveImages();
}
-}
\ No newline at end of file
+}
+
+class FakeImage implements ui.Image {
+ @override
+ void dispose() {}
+
+ @override
+ int get height => 10;
+
+ @override
+ Future<ByteData> toByteData({ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) {
+ throw UnimplementedError();
+ }
+
+ @override
+ int get width => 10;
+}