mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[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
This commit is contained in:
parent
a755c03819
commit
9a3a0dc18f
@ -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"]
|
||||
}
|
||||
} 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',
|
||||
];
|
||||
|
||||
bool get _copyAssets => true;
|
||||
|
||||
@override
|
||||
Future<void> build(Environment environment) async {
|
||||
@ -61,7 +59,6 @@ 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,
|
||||
@ -76,7 +73,6 @@ abstract class AndroidAssetBundle extends Target {
|
||||
environment.buildDir.childFile('flutter_assets.d'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Target> get dependencies => const <Target>[
|
||||
@ -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,25 +483,24 @@ 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (fullRestart) {
|
||||
|
@ -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);
|
||||
|
@ -0,0 +1,83 @@
|
||||
// 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;
|
||||
});
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user