Skip to content

Commit

Permalink
Speed up first asset load by encoding asset manifest in binary rather…
Browse files Browse the repository at this point in the history
… than JSON (#113637)
  • Loading branch information
andrewkolos committed Dec 5, 2022
1 parent d52e2de commit 56cad89
Show file tree
Hide file tree
Showing 14 changed files with 2,077 additions and 919 deletions.
Expand Up @@ -3,9 +3,9 @@
// found in the LICENSE file.

import 'dart:convert';
import 'dart:typed_data';

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart' show PlatformAssetBundle;
import 'package:flutter/services.dart' show PlatformAssetBundle, StandardMessageCodec;
import 'package:flutter/widgets.dart';

import '../common.dart';
Expand All @@ -18,16 +18,14 @@ void main() async {
final BenchmarkResultPrinter printer = BenchmarkResultPrinter();
WidgetsFlutterBinding.ensureInitialized();
final Stopwatch watch = Stopwatch();
final PlatformAssetBundle bundle = PlatformAssetBundle();

final ByteData assetManifestBytes = await bundle.load('money_asset_manifest.json');
final ByteData assetManifest = await loadAssetManifest();

watch.start();
for (int i = 0; i < _kNumIterations; i++) {
bundle.clear();
final String json = utf8.decode(assetManifestBytes.buffer.asUint8List());
// This is a test, so we don't need to worry about this rule.
// This is effectively a test.
// ignore: invalid_use_of_visible_for_testing_member
await AssetImage.manifestParser(json);
AssetImage.parseAssetManifest(assetManifest);
}
watch.stop();

Expand All @@ -40,3 +38,49 @@ void main() async {

printer.printToStdout();
}

final RegExp _extractRatioRegExp = RegExp(r'/?(\d+(\.\d*)?)x$');

Future<ByteData> loadAssetManifest() async {
double parseScale(String key) {
final Uri assetUri = Uri.parse(key);
String directoryPath = '';
if (assetUri.pathSegments.length > 1) {
directoryPath = assetUri.pathSegments[assetUri.pathSegments.length - 2];
}
final Match? match = _extractRatioRegExp.firstMatch(directoryPath);
if (match != null && match.groupCount > 0) {
return double.parse(match.group(1)!);
}
return 1.0;
}

final Map<String, dynamic> result = <String, dynamic>{};
final PlatformAssetBundle bundle = PlatformAssetBundle();

// For the benchmark, we use the older JSON format and then convert it to the modern binary format.
final ByteData jsonAssetManifestBytes = await bundle.load('money_asset_manifest.json');
final String jsonAssetManifest = utf8.decode(jsonAssetManifestBytes.buffer.asUint8List());

final Map<String, dynamic> assetManifest = json.decode(jsonAssetManifest) as Map<String, dynamic>;

for (final MapEntry<String, dynamic> manifestEntry in assetManifest.entries) {
final List<dynamic> resultVariants = <dynamic>[];
final List<String> entries = (manifestEntry.value as List<dynamic>).cast<String>();
for (final String variant in entries) {
if (variant == manifestEntry.key) {
// With the newer binary format, don't include the main asset in it's
// list of variants. This reduces parsing time at runtime.
continue;
}
final Map<String, dynamic> resultVariant = <String, dynamic>{};
final double variantDevicePixelRatio = parseScale(variant);
resultVariant['asset'] = variant;
resultVariant['dpr'] = variantDevicePixelRatio;
resultVariants.add(resultVariant);
}
result[manifestEntry.key] = resultVariants;
}

return const StandardMessageCodec().encodeMessage(result)!;
}
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_gallery/gallery/example_code_parser.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down Expand Up @@ -58,4 +60,9 @@ class TestAssetBundle extends AssetBundle {

@override
String toString() => '$runtimeType@$hashCode()';

@override
Future<T> loadStructuredBinaryData<T>(String key, FutureOr<T> Function(ByteData data) parser) async {
return parser(await load(key));
}
}

0 comments on commit 56cad89

Please sign in to comment.