From a04ab7129b373a1b887b150e8178c0846e36204a Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Thu, 26 Jan 2023 12:01:12 -0800 Subject: [PATCH] Revert "Add API for discovering assets (#118410)" (#119273) This reverts commit 2b8f2d05045590cd4aeb9f02c67baaa69db35e5e. --- .../decode_and_parse_asset_manifest.dart | 13 +- packages/flutter/lib/services.dart | 1 - .../lib/src/services/asset_bundle.dart | 75 +--------- .../lib/src/services/asset_manifest.dart | 134 ------------------ .../test/services/asset_bundle_test.dart | 81 +---------- .../test/services/asset_manifest_test.dart | 68 --------- 6 files changed, 14 insertions(+), 358 deletions(-) delete mode 100644 packages/flutter/lib/src/services/asset_manifest.dart delete mode 100644 packages/flutter/test/services/asset_manifest_test.dart diff --git a/dev/benchmarks/microbenchmarks/lib/foundation/decode_and_parse_asset_manifest.dart b/dev/benchmarks/microbenchmarks/lib/foundation/decode_and_parse_asset_manifest.dart index 5082abcf2fc0..b64c1532ce22 100644 --- a/dev/benchmarks/microbenchmarks/lib/foundation/decode_and_parse_asset_manifest.dart +++ b/dev/benchmarks/microbenchmarks/lib/foundation/decode_and_parse_asset_manifest.dart @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart' show AssetManifest, PlatformAssetBundle, rootBundle; +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart' show PlatformAssetBundle; import 'package:flutter/widgets.dart'; import '../common.dart'; @@ -15,12 +18,16 @@ void main() async { final BenchmarkResultPrinter printer = BenchmarkResultPrinter(); WidgetsFlutterBinding.ensureInitialized(); final Stopwatch watch = Stopwatch(); - final PlatformAssetBundle bundle = rootBundle as PlatformAssetBundle; + final PlatformAssetBundle bundle = PlatformAssetBundle(); + final ByteData assetManifestBytes = await bundle.load('money_asset_manifest.json'); watch.start(); for (int i = 0; i < _kNumIterations; i++) { - await AssetManifest.loadFromAssetBundle(bundle); bundle.clear(); + final String json = utf8.decode(assetManifestBytes.buffer.asUint8List()); + // This is a test, so we don't need to worry about this rule. + // ignore: invalid_use_of_visible_for_testing_member + await AssetImage.manifestParser(json); } watch.stop(); diff --git a/packages/flutter/lib/services.dart b/packages/flutter/lib/services.dart index 1b2c0b5f676f..a0ff42f6772e 100644 --- a/packages/flutter/lib/services.dart +++ b/packages/flutter/lib/services.dart @@ -11,7 +11,6 @@ library services; export 'src/services/asset_bundle.dart'; -export 'src/services/asset_manifest.dart'; export 'src/services/autofill.dart'; export 'src/services/binary_messenger.dart'; export 'src/services/binding.dart'; diff --git a/packages/flutter/lib/src/services/asset_bundle.dart b/packages/flutter/lib/src/services/asset_bundle.dart index da2719c92c6d..971fdaf58fb3 100644 --- a/packages/flutter/lib/src/services/asset_bundle.dart +++ b/packages/flutter/lib/src/services/asset_bundle.dart @@ -96,22 +96,12 @@ abstract class AssetBundle { } /// Retrieve a string from the asset bundle, parse it with the given function, - /// and return that function's result. + /// and return the function's result. /// /// Implementations may cache the result, so a particular key should only be /// used with one parser for the lifetime of the asset bundle. Future loadStructuredData(String key, Future Function(String value) parser); - /// Retrieve [ByteData] from the asset bundle, parse it with the given function, - /// and return that function's result. - /// - /// Implementations may cache the result, so a particular key should only be - /// used with one parser for the lifetime of the asset bundle. - Future loadStructuredBinaryData(String key, FutureOr Function(ByteData data) parser) async { - final ByteData data = await load(key); - return parser(data); - } - /// If this is a caching asset bundle, and the given key describes a cached /// asset, then evict the asset from the cache so that the next time it is /// loaded, the cache will be reread from the asset bundle. @@ -164,16 +154,6 @@ class NetworkAssetBundle extends AssetBundle { return parser(await loadString(key)); } - /// Retrieve [ByteData] from the asset bundle, parse it with the given function, - /// and return the function's result. - /// - /// The result is not cached. The parser is run each time the resource is - /// fetched. - @override - Future loadStructuredBinaryData(String key, FutureOr Function(ByteData data) parser) async { - return parser(await load(key)); - } - // TODO(ianh): Once the underlying network logic learns about caching, we // should implement evict(). @@ -193,7 +173,6 @@ abstract class CachingAssetBundle extends AssetBundle { // TODO(ianh): Replace this with an intelligent cache, see https://github.com/flutter/flutter/issues/3568 final Map> _stringCache = >{}; final Map> _structuredDataCache = >{}; - final Map> _structuredBinaryDataCache = >{}; @override Future loadString(String key, { bool cache = true }) { @@ -242,66 +221,16 @@ abstract class CachingAssetBundle extends AssetBundle { return completer.future; } - /// Retrieve bytedata from the asset bundle, parse it with the given function, - /// and return the function's result. - /// - /// The result of parsing the bytedata is cached (the bytedata itself is not). - /// For any given `key`, the `parser` is only run the first time. - /// - /// Once the value has been parsed, the future returned by this function for - /// subsequent calls will be a [SynchronousFuture], which resolves its - /// callback synchronously. - @override - Future loadStructuredBinaryData(String key, FutureOr Function(ByteData data) parser) { - if (_structuredBinaryDataCache.containsKey(key)) { - return _structuredBinaryDataCache[key]! as Future; - } - - // load can return a SynchronousFuture in certain cases, like in the - // flutter_test framework. So, we need to support both async and sync flows. - Completer? completer; // For async flow. - SynchronousFuture? result; // For sync flow. - - load(key) - .then(parser) - .then((T value) { - result = SynchronousFuture(value); - if (completer != null) { - // The load and parse operation ran asynchronously. We already returned - // from the loadStructuredBinaryData function and therefore the caller - // was given the future of the completer. - completer.complete(value); - } - }, onError: (Object error, StackTrace stack) { - completer!.completeError(error, stack); - }); - - if (result != null) { - // The above code ran synchronously. We can synchronously return the result. - _structuredBinaryDataCache[key] = result!; - return result!; - } - - // Since the above code is being run asynchronously and thus hasn't run its - // `then` handler yet, we'll return a completer that will be completed - // when the handler does run. - completer = Completer(); - _structuredBinaryDataCache[key] = completer.future; - return completer.future; - } - @override void evict(String key) { _stringCache.remove(key); _structuredDataCache.remove(key); - _structuredBinaryDataCache.remove(key); } @override void clear() { _stringCache.clear(); _structuredDataCache.clear(); - _structuredBinaryDataCache.clear(); } @override @@ -343,7 +272,7 @@ class PlatformAssetBundle extends CachingAssetBundle { bool debugUsePlatformChannel = false; assert(() { // dart:io is safe to use here since we early return for web - // above. If that code is changed, this needs to be guarded on + // above. If that code is changed, this needs to be gaurded on // web presence. Override how assets are loaded in tests so that // the old loader behavior that allows tests to load assets from // the current package using the package prefix. diff --git a/packages/flutter/lib/src/services/asset_manifest.dart b/packages/flutter/lib/src/services/asset_manifest.dart deleted file mode 100644 index cddf7984f85a..000000000000 --- a/packages/flutter/lib/src/services/asset_manifest.dart +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/foundation.dart'; - -import 'asset_bundle.dart'; -import 'message_codecs.dart'; - -const String _kAssetManifestFilename = 'AssetManifest.bin'; - -/// Contains details about available assets and their variants. -/// See [Asset variants](https://docs.flutter.dev/development/ui/assets-and-images#asset-variants) -/// to learn about asset variants and how to declare them. -abstract class AssetManifest { - /// Loads asset manifest data from an [AssetBundle] object and creates an - /// [AssetManifest] object from that data. - static Future loadFromAssetBundle(AssetBundle bundle) { - return bundle.loadStructuredBinaryData(_kAssetManifestFilename, _AssetManifestBin.fromStandardMessageCodecMessage); - } - - /// Lists the keys of all main assets. This does not include assets - /// that are variants of other assets. - /// - /// The logical key maps to the path of an asset specified in the pubspec.yaml - /// file at build time. - /// - /// See [Specifying assets](https://docs.flutter.dev/development/ui/assets-and-images#specifying-assets) - /// and [Loading assets](https://docs.flutter.dev/development/ui/assets-and-images#loading-assets) for more - /// information. - List listAssets(); - - /// Retrieves metadata about an asset and its variants. - /// - /// Note that this method considers a main asset to be a variant of itself and - /// includes it in the returned list. - /// - /// Throws an [ArgumentError] if [key] cannot be found within the manifest. To - /// avoid this, use a key obtained from the [listAssets] method. - List getAssetVariants(String key); -} - -// Lazily parses the binary asset manifest into a data structure that's easier to work -// with. -// -// The binary asset manifest is a map of asset keys to a list of objects -// representing the asset's variants. -// -// The entries with each variant object are: -// - "asset": the location of this variant to load it from. -// - "dpr": The device-pixel-ratio that the asset is best-suited for. -// -// New fields could be added to this object schema to support new asset variation -// features, such as themes, locale/region support, reading directions, and so on. -class _AssetManifestBin implements AssetManifest { - _AssetManifestBin(Map standardMessageData): _data = standardMessageData; - - factory _AssetManifestBin.fromStandardMessageCodecMessage(ByteData message) { - final dynamic data = const StandardMessageCodec().decodeMessage(message); - return _AssetManifestBin(data as Map); - } - - final Map _data; - final Map> _typeCastedData = >{}; - - @override - List getAssetVariants(String key) { - // We lazily delay typecasting to prevent a performance hiccup when parsing - // large asset manifests. This is important to keep an app's first asset - // load fast. - if (!_typeCastedData.containsKey(key)) { - final Object? variantData = _data[key]; - if (variantData == null) { - throw ArgumentError('Asset key $key was not found within the asset manifest.'); - } - _typeCastedData[key] = ((_data[key] ?? []) as Iterable) - .cast>() - .map((Map data) => AssetMetadata( - key: data['asset']! as String, - targetDevicePixelRatio: data['dpr']! as double, - main: false, - )) - .toList(); - - _data.remove(key); - } - - final AssetMetadata mainAsset = AssetMetadata(key: key, - targetDevicePixelRatio: null, - main: true - ); - - return [mainAsset, ..._typeCastedData[key]!]; - } - - @override - List listAssets() { - return [..._data.keys.cast(), ..._typeCastedData.keys]; - } -} - -/// Contains information about an asset. -@immutable -class AssetMetadata { - /// Creates an object containing information about an asset. - const AssetMetadata({ - required this.key, - required this.targetDevicePixelRatio, - required this.main, - }); - - /// The device pixel ratio that this asset is most ideal for. This is determined - /// by the name of the parent folder of the asset file. For example, if the - /// parent folder is named "3.0x", the target device pixel ratio of that - /// asset will be interpreted as 3. - /// - /// This will be null if the parent folder name is not a ratio value followed - /// by an "x". - /// - /// See [Declaring resolution-aware image assets](https://docs.flutter.dev/development/ui/assets-and-images#resolution-aware) - /// for more information. - final double? targetDevicePixelRatio; - - /// The asset's key, which is the path to the asset specified in the pubspec.yaml - /// file at build time. - final String key; - - /// Whether or not this is a main asset. In other words, this is true if - /// this asset is not a variant of another asset. - /// - /// See [Asset variants](https://docs.flutter.dev/development/ui/assets-and-images#asset-variants) - /// for more about asset variants. - final bool main; -} diff --git a/packages/flutter/test/services/asset_bundle_test.dart b/packages/flutter/test/services/asset_bundle_test.dart index ef00683938eb..8a97df3daa2c 100644 --- a/packages/flutter/test/services/asset_bundle_test.dart +++ b/packages/flutter/test/services/asset_bundle_test.dart @@ -14,28 +14,16 @@ class TestAssetBundle extends CachingAssetBundle { @override Future load(String key) async { - loadCallCount[key] = (loadCallCount[key] ?? 0) + 1; + loadCallCount[key] = loadCallCount[key] ?? 0 + 1; if (key == 'AssetManifest.json') { return ByteData.view(Uint8List.fromList(const Utf8Encoder().convert('{"one": ["one"]}')).buffer); } - if (key == 'AssetManifest.bin') { - return const StandardMessageCodec().encodeMessage({ - 'one': [] - })!; - } - - if (key == 'counter') { - return ByteData.view(Uint8List.fromList(const Utf8Encoder().convert(loadCallCount[key]!.toString())).buffer); - } - if (key == 'one') { return ByteData(1)..setInt8(0, 49); } - throw FlutterError('key not found'); } - } void main() { @@ -52,7 +40,7 @@ void main() { final String assetString = await bundle.loadString('one'); expect(assetString, equals('1')); - expect(bundle.loadCallCount['one'], 2); + expect(bundle.loadCallCount['one'], 1); late Object loadException; try { @@ -113,69 +101,4 @@ void main() { ), ); }, skip: isBrowser); // https://github.com/flutter/flutter/issues/56314 - - test('CachingAssetBundle caches results for loadString, loadStructuredData, and loadBinaryStructuredData', () async { - final TestAssetBundle bundle = TestAssetBundle(); - - final String firstLoadStringResult = await bundle.loadString('counter'); - final String secondLoadStringResult = await bundle.loadString('counter'); - expect(firstLoadStringResult, '1'); - expect(secondLoadStringResult, '1'); - - final String firstLoadStructuredDataResult = await bundle.loadStructuredData('AssetManifest.json', (String value) => Future.value('one')); - final String secondLoadStructuredDataResult = await bundle.loadStructuredData('AssetManifest.json', (String value) => Future.value('two')); - expect(firstLoadStructuredDataResult, 'one'); - expect(secondLoadStructuredDataResult, 'one'); - - final String firstLoadStructuredBinaryDataResult = await bundle.loadStructuredBinaryData('AssetManifest.bin', (ByteData value) => Future.value('one')); - final String secondLoadStructuredBinaryDataResult = await bundle.loadStructuredBinaryData('AssetManifest.bin', (ByteData value) => Future.value('two')); - expect(firstLoadStructuredBinaryDataResult, 'one'); - expect(secondLoadStructuredBinaryDataResult, 'one'); - }); - - test("CachingAssetBundle.clear clears all cached values'", () async { - final TestAssetBundle bundle = TestAssetBundle(); - - await bundle.loadString('counter'); - bundle.clear(); - final String secondLoadStringResult = await bundle.loadString('counter'); - expect(secondLoadStringResult, '2'); - - await bundle.loadStructuredData('AssetManifest.json', (String value) => Future.value('one')); - bundle.clear(); - final String secondLoadStructuredDataResult = await bundle.loadStructuredData('AssetManifest.json', (String value) => Future.value('two')); - expect(secondLoadStructuredDataResult, 'two'); - - await bundle.loadStructuredBinaryData('AssetManifest.bin', (ByteData value) => Future.value('one')); - bundle.clear(); - final String secondLoadStructuredBinaryDataResult = await bundle.loadStructuredBinaryData('AssetManifest.bin', (ByteData value) => Future.value('two')); - expect(secondLoadStructuredBinaryDataResult, 'two'); - }); - - test('CachingAssetBundle.evict evicts a particular key from the cache', () async { - final TestAssetBundle bundle = TestAssetBundle(); - - await bundle.loadString('counter'); - bundle.evict('counter'); - final String secondLoadStringResult = await bundle.loadString('counter'); - expect(secondLoadStringResult, '2'); - - await bundle.loadStructuredData('AssetManifest.json', (String value) => Future.value('one')); - bundle.evict('AssetManifest.json'); - final String secondLoadStructuredDataResult = await bundle.loadStructuredData('AssetManifest.json', (String value) => Future.value('two')); - expect(secondLoadStructuredDataResult, 'two'); - - await bundle.loadStructuredBinaryData('AssetManifest.bin', (ByteData value) => Future.value('one')); - bundle.evict('AssetManifest.bin'); - final String secondLoadStructuredBinaryDataResult = await bundle.loadStructuredBinaryData('AssetManifest.bin', (ByteData value) => Future.value('two')); - expect(secondLoadStructuredBinaryDataResult, 'two'); - }); - - test('loadStructuredBinaryData correctly loads ByteData', () async { - final TestAssetBundle bundle = TestAssetBundle(); - final Map assetManifest = - await bundle.loadStructuredBinaryData('AssetManifest.bin', (ByteData data) => const StandardMessageCodec().decodeMessage(data) as Map); - expect(assetManifest.keys.toList(), equals(['one'])); - expect(assetManifest['one'], []); - }); } diff --git a/packages/flutter/test/services/asset_manifest_test.dart b/packages/flutter/test/services/asset_manifest_test.dart deleted file mode 100644 index 4bd9c92223f0..000000000000 --- a/packages/flutter/test/services/asset_manifest_test.dart +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; - -class TestAssetBundle extends AssetBundle { - @override - Future load(String key) async { - if (key == 'AssetManifest.bin') { - final Map> binManifestData = >{ - 'assets/foo.png': [ - { - 'asset': 'assets/2x/foo.png', - 'dpr': 2.0 - } - ], - 'assets/bar.png': [], - }; - - final ByteData data = const StandardMessageCodec().encodeMessage(binManifestData)!; - return data; - } - - throw ArgumentError('Unexpected key'); - } - - @override - Future loadStructuredData(String key, Future Function(String value) parser) async { - return parser(await loadString(key)); - } -} - - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - test('loadFromBundle correctly parses a binary asset manifest', () async { - final AssetManifest manifest = await AssetManifest.loadFromAssetBundle(TestAssetBundle()); - - expect(manifest.listAssets(), unorderedEquals(['assets/foo.png', 'assets/bar.png'])); - - final List fooVariants = manifest.getAssetVariants('assets/foo.png'); - expect(fooVariants.length, 2); - final AssetMetadata firstFooVariant = fooVariants[0]; - expect(firstFooVariant.key, 'assets/foo.png'); - expect(firstFooVariant.targetDevicePixelRatio, null); - expect(firstFooVariant.main, true); - final AssetMetadata secondFooVariant = fooVariants[1]; - expect(secondFooVariant.key, 'assets/2x/foo.png'); - expect(secondFooVariant.targetDevicePixelRatio, 2.0); - expect(secondFooVariant.main, false); - - final List barVariants = manifest.getAssetVariants('assets/bar.png'); - expect(barVariants.length, 1); - final AssetMetadata firstBarVariant = barVariants[0]; - expect(firstBarVariant.key, 'assets/bar.png'); - expect(firstBarVariant.targetDevicePixelRatio, null); - expect(firstBarVariant.main, true); - }); - - test('getAssetVariants throws if given a key not contained in the asset manifest', () async { - final AssetManifest manifest = await AssetManifest.loadFromAssetBundle(TestAssetBundle()); - - expect(() => manifest.getAssetVariants('invalid asset key'), throwsArgumentError); - }); -}