Unverified Commit 9a3a0dc1 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] hot reload/restart update for asset manager change (#66742)

Do not upload all assets on initial devFS sync. This should increase the reliability of the initial connection, even in the face of flaky devfs behavior, in addition to a moderate perf improvement.

Updates fast-start to build assets as part of the initial bundle

Requires flutter/engine#21436
Requires flutter/engine#21586
Requires flutter/engine#21611
parent a755c038
......@@ -893,11 +893,7 @@ abstract class BaseFlutterTask extends DefaultTask {
// cache.
String[] ruleNames;
if (buildMode == "debug") {
if (fastStart) {
ruleNames = ["faststart_android_application"]
} else {
ruleNames = ["debug_android_application"]
}
ruleNames = ["debug_android_application"]
} else {
ruleNames = targetPlatformValues.collect { "android_aot_bundle_${buildMode}_$it" }
}
......
......@@ -34,11 +34,9 @@ abstract class AndroidAssetBundle extends Target {
@override
List<String> get depfiles => <String>[
if (_copyAssets)
'flutter_assets.d',
'flutter_assets.d',
];
bool get _copyAssets => true;
@override
Future<void> build(Environment environment) async {
......@@ -61,21 +59,19 @@ abstract class AndroidAssetBundle extends Target {
environment.fileSystem.file(isolateSnapshotData)
.copySync(outputDirectory.childFile('isolate_snapshot_data').path);
}
if (_copyAssets) {
final Depfile assetDepfile = await copyAssets(
environment,
outputDirectory,
targetPlatform: TargetPlatform.android,
);
final DepfileService depfileService = DepfileService(
fileSystem: environment.fileSystem,
logger: environment.logger,
);
depfileService.writeToFile(
assetDepfile,
environment.buildDir.childFile('flutter_assets.d'),
);
}
final Depfile assetDepfile = await copyAssets(
environment,
outputDirectory,
targetPlatform: TargetPlatform.android,
);
final DepfileService depfileService = DepfileService(
fileSystem: environment.fileSystem,
logger: environment.logger,
);
depfileService.writeToFile(
assetDepfile,
environment.buildDir.childFile('flutter_assets.d'),
);
}
@override
......@@ -108,17 +104,6 @@ class DebugAndroidApplication extends AndroidAssetBundle {
];
}
/// A minimal android application that does not include assets.
class FastStartAndroidApplication extends DebugAndroidApplication {
const FastStartAndroidApplication();
@override
String get name => 'faststart_android_application';
@override
bool get _copyAssets => false;
}
/// An implementation of [AndroidAssetBundle] that only includes assets.
class AotAndroidAssetBundle extends AndroidAssetBundle {
const AotAndroidAssetBundle();
......
......@@ -49,7 +49,6 @@ const List<Target> _kDefaultTargets = <Target>[
CopyFlutterBundle(),
// Android targets,
DebugAndroidApplication(),
FastStartAndroidApplication(),
ProfileAndroidApplication(),
// Android ABI specific AOT rules.
androidArmProfileBundle,
......
......@@ -472,7 +472,6 @@ class DevFS {
String dillOutputPath,
bool fullRestart = false,
String projectRootPath,
bool skipAssets = false,
}) async {
assert(trackWidgetCreation != null);
assert(generator != null);
......@@ -484,24 +483,23 @@ class DevFS {
final Map<Uri, DevFSContent> dirtyEntries = <Uri, DevFSContent>{};
int syncedBytes = 0;
if (bundle != null && !skipAssets) {
if (bundle != null) {
final String assetBuildDirPrefix = _asUriPath(getAssetBuildDirectory());
// We write the assets into the AssetBundle working dir so that they
// The tool writes the assets into the AssetBundle working dir so that they
// are in the same location in DevFS and the iOS simulator.
final String assetDirectory = getAssetBuildDirectory();
bundle.entries.forEach((String archivePath, DevFSContent content) {
if (!content.isModified || bundleFirstUpload) {
return;
}
final Uri deviceUri = _fileSystem.path.toUri(_fileSystem.path.join(assetDirectory, archivePath));
if (deviceUri.path.startsWith(assetBuildDirPrefix)) {
archivePath = deviceUri.path.substring(assetBuildDirPrefix.length);
}
// Only update assets if they have been modified, or if this is the
// first upload of the asset bundle.
if (content.isModified || (bundleFirstUpload && archivePath != null)) {
dirtyEntries[deviceUri] = content;
syncedBytes += content.size;
if (archivePath != null && !bundleFirstUpload) {
assetPathsToEvict.add(archivePath);
}
dirtyEntries[deviceUri] = content;
syncedBytes += content.size;
if (archivePath != null && !bundleFirstUpload) {
assetPathsToEvict.add(archivePath);
}
});
}
......
......@@ -818,7 +818,6 @@ class WebDevFS implements DevFS {
String dillOutputPath,
bool fullRestart = false,
String projectRootPath,
bool skipAssets = false,
}) async {
assert(trackWidgetCreation != null);
assert(generator != null);
......
// 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 'dart:async';
import 'package:file/file.dart';
import '../src/common.dart';
import 'test_data/hot_reload_with_asset.dart';
import 'test_driver.dart';
import 'test_utils.dart';
void main() {
Directory tempDir;
final HotReloadWithAssetProject project = HotReloadWithAssetProject();
FlutterRunTestDriver flutter;
setUp(() async {
tempDir = createResolvedTempDirectorySync('hot_reload_test.');
await project.setUpIn(tempDir);
flutter = FlutterRunTestDriver(tempDir);
});
tearDown(() async {
await flutter?.stop();
tryToDelete(tempDir);
});
testWithoutContext('hot reload does not need to sync assets on the first reload', () async {
final Completer<void> onFirstLoad = Completer<void>();
final Completer<void> onSecondLoad = Completer<void>();
flutter.stdout.listen((String line) {
// If the asset fails to load, this message will be printed instead.
// this indicates that the devFS was not able to locate the asset
// after the hot reload.
if (line.contains('FAILED TO LOAD')) {
fail('Did not load asset: $line');
}
if (line.contains('LOADED DATA')) {
onFirstLoad.complete();
}
if (line.contains('SECOND DATA')) {
onSecondLoad.complete();
}
});
flutter.stdout.listen(print);
await flutter.run();
await onFirstLoad.future;
project.uncommentHotReloadPrint();
await flutter.hotReload();
await onSecondLoad.future;
});
testWithoutContext('hot restart does not need to sync assets on the first reload', () async {
final Completer<void> onFirstLoad = Completer<void>();
final Completer<void> onSecondLoad = Completer<void>();
flutter.stdout.listen((String line) {
// If the asset fails to load, this message will be printed instead.
// this indicates that the devFS was not able to locate the asset
// after the hot reload.
if (line.contains('FAILED TO LOAD')) {
fail('Did not load asset: $line');
}
if (line.contains('LOADED DATA')) {
onFirstLoad.complete();
}
if (line.contains('SECOND DATA')) {
onSecondLoad.complete();
}
});
flutter.stdout.listen(print);
await flutter.run();
await onFirstLoad.future;
project.uncommentHotReloadPrint();
await flutter.hotRestart();
await onSecondLoad.future;
});
}
// 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 '../test_utils.dart';
import 'project.dart';
class HotReloadWithAssetProject extends Project {
@override
final String pubspec = '''
name: test
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
flutter:
assets:
- pubspec.yaml
''';
@override
final String main = r'''
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final ByteData message = const StringCodec().encodeMessage('AppLifecycleState.resumed');
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
rootBundle.evict('pubspec.yaml');
rootBundle.load('pubspec.yaml').then((_) {
print('LOADED DATA');
}, onError: (dynamic error, StackTrace stackTrace) {
print('FAILED TO LOAD');
});
return Container();
}
}
''';
void uncommentHotReloadPrint() {
final String newMainContents = main.replaceAll(
'LOADED DATA',
'SECOND DATA',
);
writeFile(fileSystem.path.join(dir.path, 'lib', 'main.dart'), newMainContents);
}
}
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