diff --git a/packages/flutter/lib/src/painting/binding.dart b/packages/flutter/lib/src/painting/binding.dart index d5f00a981d..900d68ea31 100644 --- a/packages/flutter/lib/src/painting/binding.dart +++ b/packages/flutter/lib/src/painting/binding.dart @@ -99,6 +99,12 @@ mixin PaintingBinding on BindingBase, ServicesBinding { 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 227b9070d3..0c4ceede79 100644 --- a/packages/flutter/lib/src/services/binding.dart +++ b/packages/flutter/lib/src/services/binding.dart @@ -48,13 +48,32 @@ mixin ServicesBinding on BindingBase { 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 handleSystemMessage(Object systemMessage) async { } + Future handleSystemMessage(Object systemMessage) async { + final Map message = systemMessage as Map; + 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 d511001d6e..a8f01839d4 100644 --- a/packages/flutter/lib/src/widgets/binding.dart +++ b/packages/flutter/lib/src/widgets/binding.dart @@ -655,32 +655,13 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB 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 handleSystemMessage(Object systemMessage) async { - await super.handleSystemMessage(systemMessage); - final Map message = systemMessage as Map; - final String type = message['type'] as String; - switch (type) { - case 'memoryPressure': - handleMemoryPressure(); - break; - } - return; - } - bool _needToReportFirstFrame = true; final Completer _firstFrameCompleter = Completer(); diff --git a/packages/flutter/test/painting/binding_test.dart b/packages/flutter/test/painting/binding_test.dart index f216b1ade8..74b551519b 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_test/flutter_test.dart'; 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.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( + {'type': 'memoryPressure'}); + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/system', message, (_) { }); + expect(imageCache.currentSize, 0); }); test('evict clears live references', () async { @@ -84,7 +84,7 @@ class TestBindingBase implements BindingBase { 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 @@ class FakeImageCache extends ImageCache { 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 toByteData({ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) { + throw UnimplementedError(); + } + + @override + int get width => 10; +}