Unverified Commit 14f3a36a authored by Dan Field's avatar Dan Field Committed by GitHub

Clear ImageCache on MemoryPressure (#53959)

parent 4b92c167
...@@ -99,6 +99,12 @@ mixin PaintingBinding on BindingBase, ServicesBinding { ...@@ -99,6 +99,12 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
imageCache.clearLiveImages(); imageCache.clearLiveImages();
} }
@override
void handleMemoryPressure() {
super.handleMemoryPressure();
imageCache.clear();
}
/// Listenable that notifies when the available fonts on the system have /// Listenable that notifies when the available fonts on the system have
/// changed. /// changed.
/// ///
......
...@@ -48,13 +48,32 @@ mixin ServicesBinding on BindingBase { ...@@ -48,13 +48,32 @@ mixin ServicesBinding on BindingBase {
return const _DefaultBinaryMessenger._(); 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] /// Handler called for messages received on the [SystemChannels.system]
/// message channel. /// message channel.
/// ///
/// Other bindings may override this to respond to incoming system messages. /// Other bindings may override this to respond to incoming system messages.
@protected @protected
@mustCallSuper @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]. /// Adds relevant licenses to the [LicenseRegistry].
/// ///
......
...@@ -655,32 +655,13 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB ...@@ -655,32 +655,13 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
observer.didChangeAppLifecycleState(state); observer.didChangeAppLifecycleState(state);
} }
/// Called when the operating system notifies the application of a memory @override
/// pressure situation.
///
/// Notifies all the observers using
/// [WidgetsBindingObserver.didHaveMemoryPressure].
///
/// This method exposes the `memoryPressure` notification from
/// [SystemChannels.system].
void handleMemoryPressure() { void handleMemoryPressure() {
super.handleMemoryPressure();
for (final WidgetsBindingObserver observer in _observers) for (final WidgetsBindingObserver observer in _observers)
observer.didHaveMemoryPressure(); 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; bool _needToReportFirstFrame = true;
final Completer<void> _firstFrameCompleter = Completer<void>(); final Completer<void> _firstFrameCompleter = Completer<void>();
......
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:typed_data' show Uint8List; import 'dart:ui' as ui;
import 'dart:ui';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
...@@ -11,21 +10,22 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -11,21 +10,22 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
import 'image_data.dart';
import 'painting_utils.dart';
void main() { void main() {
final PaintingBindingSpy binding = PaintingBindingSpy(); testWidgets('didHaveMemoryPressure clears imageCache', (WidgetTester tester) async {
imageCache.putIfAbsent(1, () => OneFrameImageStreamCompleter(
test('instantiateImageCodec used for loading images', () async { Future<ImageInfo>.value(ImageInfo(
expect(binding.instantiateImageCodecCalledCount, 0); image: FakeImage(),
scale: 1.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); await tester.idle();
}); expect(imageCache.currentSize, 1);
expect(binding.instantiateImageCodecCalledCount, 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 { test('evict clears live references', () async {
...@@ -84,7 +84,7 @@ class TestBindingBase implements BindingBase { ...@@ -84,7 +84,7 @@ class TestBindingBase implements BindingBase {
void unlocked() {} void unlocked() {}
@override @override
Window get window => throw UnimplementedError(); ui.Window get window => throw UnimplementedError();
} }
class TestPaintingBinding extends TestBindingBase with ServicesBinding, PaintingBinding { class TestPaintingBinding extends TestBindingBase with ServicesBinding, PaintingBinding {
...@@ -111,4 +111,20 @@ class FakeImageCache extends ImageCache { ...@@ -111,4 +111,20 @@ class FakeImageCache extends ImageCache {
liveClearCount += 1; liveClearCount += 1;
super.clearLiveImages(); 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment