Skip to content

Commit 9a3a0dc

Browse files
author
Jonah Williams
authored
[flutter_tools] hot reload/restart update for asset manager change (flutter#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
1 parent a755c03 commit 9a3a0dc

File tree

7 files changed

+165
-47
lines changed

7 files changed

+165
-47
lines changed

packages/flutter_tools/gradle/flutter.gradle

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -893,11 +893,7 @@ abstract class BaseFlutterTask extends DefaultTask {
893893
// cache.
894894
String[] ruleNames;
895895
if (buildMode == "debug") {
896-
if (fastStart) {
897-
ruleNames = ["faststart_android_application"]
898-
} else {
899-
ruleNames = ["debug_android_application"]
900-
}
896+
ruleNames = ["debug_android_application"]
901897
} else {
902898
ruleNames = targetPlatformValues.collect { "android_aot_bundle_${buildMode}_$it" }
903899
}

packages/flutter_tools/lib/src/build_system/targets/android.dart

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,9 @@ abstract class AndroidAssetBundle extends Target {
3434

3535
@override
3636
List<String> get depfiles => <String>[
37-
if (_copyAssets)
38-
'flutter_assets.d',
37+
'flutter_assets.d',
3938
];
4039

41-
bool get _copyAssets => true;
4240

4341
@override
4442
Future<void> build(Environment environment) async {
@@ -61,21 +59,19 @@ abstract class AndroidAssetBundle extends Target {
6159
environment.fileSystem.file(isolateSnapshotData)
6260
.copySync(outputDirectory.childFile('isolate_snapshot_data').path);
6361
}
64-
if (_copyAssets) {
65-
final Depfile assetDepfile = await copyAssets(
66-
environment,
67-
outputDirectory,
68-
targetPlatform: TargetPlatform.android,
69-
);
70-
final DepfileService depfileService = DepfileService(
71-
fileSystem: environment.fileSystem,
72-
logger: environment.logger,
73-
);
74-
depfileService.writeToFile(
75-
assetDepfile,
76-
environment.buildDir.childFile('flutter_assets.d'),
77-
);
78-
}
62+
final Depfile assetDepfile = await copyAssets(
63+
environment,
64+
outputDirectory,
65+
targetPlatform: TargetPlatform.android,
66+
);
67+
final DepfileService depfileService = DepfileService(
68+
fileSystem: environment.fileSystem,
69+
logger: environment.logger,
70+
);
71+
depfileService.writeToFile(
72+
assetDepfile,
73+
environment.buildDir.childFile('flutter_assets.d'),
74+
);
7975
}
8076

8177
@override
@@ -108,17 +104,6 @@ class DebugAndroidApplication extends AndroidAssetBundle {
108104
];
109105
}
110106

111-
/// A minimal android application that does not include assets.
112-
class FastStartAndroidApplication extends DebugAndroidApplication {
113-
const FastStartAndroidApplication();
114-
115-
@override
116-
String get name => 'faststart_android_application';
117-
118-
@override
119-
bool get _copyAssets => false;
120-
}
121-
122107
/// An implementation of [AndroidAssetBundle] that only includes assets.
123108
class AotAndroidAssetBundle extends AndroidAssetBundle {
124109
const AotAndroidAssetBundle();

packages/flutter_tools/lib/src/commands/assemble.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ const List<Target> _kDefaultTargets = <Target>[
4949
CopyFlutterBundle(),
5050
// Android targets,
5151
DebugAndroidApplication(),
52-
FastStartAndroidApplication(),
5352
ProfileAndroidApplication(),
5453
// Android ABI specific AOT rules.
5554
androidArmProfileBundle,

packages/flutter_tools/lib/src/devfs.dart

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,6 @@ class DevFS {
472472
String dillOutputPath,
473473
bool fullRestart = false,
474474
String projectRootPath,
475-
bool skipAssets = false,
476475
}) async {
477476
assert(trackWidgetCreation != null);
478477
assert(generator != null);
@@ -484,24 +483,23 @@ class DevFS {
484483
final Map<Uri, DevFSContent> dirtyEntries = <Uri, DevFSContent>{};
485484

486485
int syncedBytes = 0;
487-
if (bundle != null && !skipAssets) {
486+
if (bundle != null) {
488487
final String assetBuildDirPrefix = _asUriPath(getAssetBuildDirectory());
489-
// We write the assets into the AssetBundle working dir so that they
488+
// The tool writes the assets into the AssetBundle working dir so that they
490489
// are in the same location in DevFS and the iOS simulator.
491490
final String assetDirectory = getAssetBuildDirectory();
492491
bundle.entries.forEach((String archivePath, DevFSContent content) {
492+
if (!content.isModified || bundleFirstUpload) {
493+
return;
494+
}
493495
final Uri deviceUri = _fileSystem.path.toUri(_fileSystem.path.join(assetDirectory, archivePath));
494496
if (deviceUri.path.startsWith(assetBuildDirPrefix)) {
495497
archivePath = deviceUri.path.substring(assetBuildDirPrefix.length);
496498
}
497-
// Only update assets if they have been modified, or if this is the
498-
// first upload of the asset bundle.
499-
if (content.isModified || (bundleFirstUpload && archivePath != null)) {
500-
dirtyEntries[deviceUri] = content;
501-
syncedBytes += content.size;
502-
if (archivePath != null && !bundleFirstUpload) {
503-
assetPathsToEvict.add(archivePath);
504-
}
499+
dirtyEntries[deviceUri] = content;
500+
syncedBytes += content.size;
501+
if (archivePath != null && !bundleFirstUpload) {
502+
assetPathsToEvict.add(archivePath);
505503
}
506504
});
507505
}

packages/flutter_tools/lib/src/isolated/devfs_web.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,6 @@ class WebDevFS implements DevFS {
818818
String dillOutputPath,
819819
bool fullRestart = false,
820820
String projectRootPath,
821-
bool skipAssets = false,
822821
}) async {
823822
assert(trackWidgetCreation != null);
824823
assert(generator != null);
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:file/file.dart';
8+
9+
import '../src/common.dart';
10+
import 'test_data/hot_reload_with_asset.dart';
11+
import 'test_driver.dart';
12+
import 'test_utils.dart';
13+
14+
void main() {
15+
Directory tempDir;
16+
final HotReloadWithAssetProject project = HotReloadWithAssetProject();
17+
FlutterRunTestDriver flutter;
18+
19+
setUp(() async {
20+
tempDir = createResolvedTempDirectorySync('hot_reload_test.');
21+
await project.setUpIn(tempDir);
22+
flutter = FlutterRunTestDriver(tempDir);
23+
});
24+
25+
tearDown(() async {
26+
await flutter?.stop();
27+
tryToDelete(tempDir);
28+
});
29+
30+
testWithoutContext('hot reload does not need to sync assets on the first reload', () async {
31+
final Completer<void> onFirstLoad = Completer<void>();
32+
final Completer<void> onSecondLoad = Completer<void>();
33+
34+
flutter.stdout.listen((String line) {
35+
// If the asset fails to load, this message will be printed instead.
36+
// this indicates that the devFS was not able to locate the asset
37+
// after the hot reload.
38+
if (line.contains('FAILED TO LOAD')) {
39+
fail('Did not load asset: $line');
40+
}
41+
if (line.contains('LOADED DATA')) {
42+
onFirstLoad.complete();
43+
}
44+
if (line.contains('SECOND DATA')) {
45+
onSecondLoad.complete();
46+
}
47+
});
48+
flutter.stdout.listen(print);
49+
await flutter.run();
50+
await onFirstLoad.future;
51+
52+
project.uncommentHotReloadPrint();
53+
await flutter.hotReload();
54+
await onSecondLoad.future;
55+
});
56+
57+
testWithoutContext('hot restart does not need to sync assets on the first reload', () async {
58+
final Completer<void> onFirstLoad = Completer<void>();
59+
final Completer<void> onSecondLoad = Completer<void>();
60+
61+
flutter.stdout.listen((String line) {
62+
// If the asset fails to load, this message will be printed instead.
63+
// this indicates that the devFS was not able to locate the asset
64+
// after the hot reload.
65+
if (line.contains('FAILED TO LOAD')) {
66+
fail('Did not load asset: $line');
67+
}
68+
if (line.contains('LOADED DATA')) {
69+
onFirstLoad.complete();
70+
}
71+
if (line.contains('SECOND DATA')) {
72+
onSecondLoad.complete();
73+
}
74+
});
75+
flutter.stdout.listen(print);
76+
await flutter.run();
77+
await onFirstLoad.future;
78+
79+
project.uncommentHotReloadPrint();
80+
await flutter.hotRestart();
81+
await onSecondLoad.future;
82+
});
83+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import '../test_utils.dart';
6+
import 'project.dart';
7+
8+
class HotReloadWithAssetProject extends Project {
9+
@override
10+
final String pubspec = '''
11+
name: test
12+
environment:
13+
sdk: ">=2.0.0-dev.68.0 <3.0.0"
14+
15+
dependencies:
16+
flutter:
17+
sdk: flutter
18+
19+
flutter:
20+
assets:
21+
- pubspec.yaml
22+
''';
23+
24+
@override
25+
final String main = r'''
26+
import 'package:flutter/material.dart';
27+
import 'package:flutter/services.dart';
28+
import 'package:flutter/widgets.dart';
29+
30+
Future<void> main() async {
31+
WidgetsFlutterBinding.ensureInitialized();
32+
final ByteData message = const StringCodec().encodeMessage('AppLifecycleState.resumed');
33+
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
34+
runApp(MyApp());
35+
}
36+
37+
class MyApp extends StatelessWidget {
38+
@override
39+
Widget build(BuildContext context) {
40+
rootBundle.evict('pubspec.yaml');
41+
rootBundle.load('pubspec.yaml').then((_) {
42+
print('LOADED DATA');
43+
}, onError: (dynamic error, StackTrace stackTrace) {
44+
print('FAILED TO LOAD');
45+
});
46+
return Container();
47+
}
48+
}
49+
''';
50+
51+
void uncommentHotReloadPrint() {
52+
final String newMainContents = main.replaceAll(
53+
'LOADED DATA',
54+
'SECOND DATA',
55+
);
56+
writeFile(fileSystem.path.join(dir.path, 'lib', 'main.dart'), newMainContents);
57+
}
58+
}

0 commit comments

Comments
 (0)