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 { ...@@ -893,11 +893,7 @@ abstract class BaseFlutterTask extends DefaultTask {
// cache. // cache.
String[] ruleNames; String[] ruleNames;
if (buildMode == "debug") { if (buildMode == "debug") {
if (fastStart) { ruleNames = ["debug_android_application"]
ruleNames = ["faststart_android_application"]
} else {
ruleNames = ["debug_android_application"]
}
} else { } else {
ruleNames = targetPlatformValues.collect { "android_aot_bundle_${buildMode}_$it" } ruleNames = targetPlatformValues.collect { "android_aot_bundle_${buildMode}_$it" }
} }
......
...@@ -34,11 +34,9 @@ abstract class AndroidAssetBundle extends Target { ...@@ -34,11 +34,9 @@ abstract class AndroidAssetBundle extends Target {
@override @override
List<String> get depfiles => <String>[ List<String> get depfiles => <String>[
if (_copyAssets) 'flutter_assets.d',
'flutter_assets.d',
]; ];
bool get _copyAssets => true;
@override @override
Future<void> build(Environment environment) async { Future<void> build(Environment environment) async {
...@@ -61,21 +59,19 @@ abstract class AndroidAssetBundle extends Target { ...@@ -61,21 +59,19 @@ abstract class AndroidAssetBundle extends Target {
environment.fileSystem.file(isolateSnapshotData) environment.fileSystem.file(isolateSnapshotData)
.copySync(outputDirectory.childFile('isolate_snapshot_data').path); .copySync(outputDirectory.childFile('isolate_snapshot_data').path);
} }
if (_copyAssets) { final Depfile assetDepfile = await copyAssets(
final Depfile assetDepfile = await copyAssets( environment,
environment, outputDirectory,
outputDirectory, targetPlatform: TargetPlatform.android,
targetPlatform: TargetPlatform.android, );
); final DepfileService depfileService = DepfileService(
final DepfileService depfileService = DepfileService( fileSystem: environment.fileSystem,
fileSystem: environment.fileSystem, logger: environment.logger,
logger: environment.logger, );
); depfileService.writeToFile(
depfileService.writeToFile( assetDepfile,
assetDepfile, environment.buildDir.childFile('flutter_assets.d'),
environment.buildDir.childFile('flutter_assets.d'), );
);
}
} }
@override @override
...@@ -108,17 +104,6 @@ class DebugAndroidApplication extends AndroidAssetBundle { ...@@ -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. /// An implementation of [AndroidAssetBundle] that only includes assets.
class AotAndroidAssetBundle extends AndroidAssetBundle { class AotAndroidAssetBundle extends AndroidAssetBundle {
const AotAndroidAssetBundle(); const AotAndroidAssetBundle();
......
...@@ -49,7 +49,6 @@ const List<Target> _kDefaultTargets = <Target>[ ...@@ -49,7 +49,6 @@ const List<Target> _kDefaultTargets = <Target>[
CopyFlutterBundle(), CopyFlutterBundle(),
// Android targets, // Android targets,
DebugAndroidApplication(), DebugAndroidApplication(),
FastStartAndroidApplication(),
ProfileAndroidApplication(), ProfileAndroidApplication(),
// Android ABI specific AOT rules. // Android ABI specific AOT rules.
androidArmProfileBundle, androidArmProfileBundle,
......
...@@ -472,7 +472,6 @@ class DevFS { ...@@ -472,7 +472,6 @@ class DevFS {
String dillOutputPath, String dillOutputPath,
bool fullRestart = false, bool fullRestart = false,
String projectRootPath, String projectRootPath,
bool skipAssets = false,
}) async { }) async {
assert(trackWidgetCreation != null); assert(trackWidgetCreation != null);
assert(generator != null); assert(generator != null);
...@@ -484,24 +483,23 @@ class DevFS { ...@@ -484,24 +483,23 @@ class DevFS {
final Map<Uri, DevFSContent> dirtyEntries = <Uri, DevFSContent>{}; final Map<Uri, DevFSContent> dirtyEntries = <Uri, DevFSContent>{};
int syncedBytes = 0; int syncedBytes = 0;
if (bundle != null && !skipAssets) { if (bundle != null) {
final String assetBuildDirPrefix = _asUriPath(getAssetBuildDirectory()); 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. // are in the same location in DevFS and the iOS simulator.
final String assetDirectory = getAssetBuildDirectory(); final String assetDirectory = getAssetBuildDirectory();
bundle.entries.forEach((String archivePath, DevFSContent content) { bundle.entries.forEach((String archivePath, DevFSContent content) {
if (!content.isModified || bundleFirstUpload) {
return;
}
final Uri deviceUri = _fileSystem.path.toUri(_fileSystem.path.join(assetDirectory, archivePath)); final Uri deviceUri = _fileSystem.path.toUri(_fileSystem.path.join(assetDirectory, archivePath));
if (deviceUri.path.startsWith(assetBuildDirPrefix)) { if (deviceUri.path.startsWith(assetBuildDirPrefix)) {
archivePath = deviceUri.path.substring(assetBuildDirPrefix.length); archivePath = deviceUri.path.substring(assetBuildDirPrefix.length);
} }
// Only update assets if they have been modified, or if this is the dirtyEntries[deviceUri] = content;
// first upload of the asset bundle. syncedBytes += content.size;
if (content.isModified || (bundleFirstUpload && archivePath != null)) { if (archivePath != null && !bundleFirstUpload) {
dirtyEntries[deviceUri] = content; assetPathsToEvict.add(archivePath);
syncedBytes += content.size;
if (archivePath != null && !bundleFirstUpload) {
assetPathsToEvict.add(archivePath);
}
} }
}); });
} }
......
...@@ -818,7 +818,6 @@ class WebDevFS implements DevFS { ...@@ -818,7 +818,6 @@ class WebDevFS implements DevFS {
String dillOutputPath, String dillOutputPath,
bool fullRestart = false, bool fullRestart = false,
String projectRootPath, String projectRootPath,
bool skipAssets = false,
}) async { }) async {
assert(trackWidgetCreation != null); assert(trackWidgetCreation != null);
assert(generator != 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