diff --git a/packages/flutter/lib/src/services/asset_bundle.dart b/packages/flutter/lib/src/services/asset_bundle.dart index 9d02a728ed..16b933fa90 100644 --- a/packages/flutter/lib/src/services/asset_bundle.dart +++ b/packages/flutter/lib/src/services/asset_bundle.dart @@ -98,6 +98,9 @@ abstract class AssetBundle { /// loaded, the cache will be reread from the asset bundle. void evict(String key) { } + /// If this is a caching asset bundle, clear all cached data. + void clear() { } + @override String toString() => '${describeIdentity(this)}()'; } @@ -215,6 +218,12 @@ abstract class CachingAssetBundle extends AssetBundle { _stringCache.remove(key); _structuredDataCache.remove(key); } + + @override + void clear() { + _stringCache.clear(); + _structuredDataCache.clear(); + } } /// An [AssetBundle] that loads resources using platform messages. diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart index 7e0dd397f6..ef612f285c 100644 --- a/packages/flutter/lib/src/services/binding.dart +++ b/packages/flutter/lib/src/services/binding.dart @@ -113,7 +113,9 @@ mixin ServicesBinding on BindingBase, SchedulerBinding { /// [SystemChannels.system]. @protected @mustCallSuper - void handleMemoryPressure() { } + void handleMemoryPressure() { + rootBundle.clear(); + } /// Handler called for messages received on the [SystemChannels.system] /// message channel. diff --git a/packages/flutter/test/services/binding_test.dart b/packages/flutter/test/services/binding_test.dart index ff92a14baa..f7ad7820b3 100644 --- a/packages/flutter/test/services/binding_test.dart +++ b/packages/flutter/test/services/binding_test.dart @@ -51,8 +51,10 @@ class TestBinding extends BindingBase with SchedulerBinding, ServicesBinding { } void main() { + final TestBinding binding = TestBinding(); + test('Adds rootBundle LICENSES to LicenseRegistry', () async { - TestBinding().defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) async { + binding.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) async { if (const StringCodec().decodeMessage(message) == 'NOTICES.Z' && !kIsWeb) { return Uint8List.fromList(gzip.encode(utf8.encode(combinedLicenses))).buffer.asByteData(); } @@ -76,4 +78,33 @@ void main() { equals(['L2Paragraph1', 'L2Paragraph2', 'L2Paragraph3']), ); }); + + test('didHaveMemoryPressure clears asset caches', () async { + int flutterAssetsCallCount = 0; + binding.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) async { + flutterAssetsCallCount += 1; + return Uint8List.fromList('test_asset_data'.codeUnits).buffer.asByteData(); + }); + + await rootBundle.loadString('test_asset'); + expect(flutterAssetsCallCount, 1); + await rootBundle.loadString('test_asset2'); + expect(flutterAssetsCallCount, 2); + await rootBundle.loadString('test_asset'); + expect(flutterAssetsCallCount, 2); + await rootBundle.loadString('test_asset2'); + expect(flutterAssetsCallCount, 2); + + final ByteData message = const JSONMessageCodec().encodeMessage({'type': 'memoryPressure'})!; + await ServicesBinding.instance!.defaultBinaryMessenger.handlePlatformMessage('flutter/system', message, (_) { }); + + await rootBundle.loadString('test_asset'); + expect(flutterAssetsCallCount, 3); + await rootBundle.loadString('test_asset2'); + expect(flutterAssetsCallCount, 4); + await rootBundle.loadString('test_asset'); + expect(flutterAssetsCallCount, 4); + await rootBundle.loadString('test_asset2'); + expect(flutterAssetsCallCount, 4); + }); }