[flutter_tools] support hot reload of font assets (#109091)
This commit is contained in:
parent
f9391522f3
commit
921c80302c
@ -20,6 +20,8 @@ import 'compile.dart';
|
|||||||
import 'convert.dart' show base64, utf8;
|
import 'convert.dart' show base64, utf8;
|
||||||
import 'vmservice.dart';
|
import 'vmservice.dart';
|
||||||
|
|
||||||
|
const String _kFontManifest = 'FontManifest.json';
|
||||||
|
|
||||||
class DevFSConfig {
|
class DevFSConfig {
|
||||||
/// Should DevFS assume that symlink targets are stable?
|
/// Should DevFS assume that symlink targets are stable?
|
||||||
bool cacheSymlinks = false;
|
bool cacheSymlinks = false;
|
||||||
@ -485,6 +487,9 @@ class DevFS {
|
|||||||
// A flag to indicate whether we have called `setAssetDirectory` on the target device.
|
// A flag to indicate whether we have called `setAssetDirectory` on the target device.
|
||||||
bool hasSetAssetDirectory = false;
|
bool hasSetAssetDirectory = false;
|
||||||
|
|
||||||
|
/// Whether the font manifest was uploaded during [update].
|
||||||
|
bool didUpdateFontManifest = false;
|
||||||
|
|
||||||
List<Uri> sources = <Uri>[];
|
List<Uri> sources = <Uri>[];
|
||||||
DateTime? lastCompiled;
|
DateTime? lastCompiled;
|
||||||
DateTime? _previousCompiled;
|
DateTime? _previousCompiled;
|
||||||
@ -589,6 +594,7 @@ class DevFS {
|
|||||||
assert(trackWidgetCreation != null);
|
assert(trackWidgetCreation != null);
|
||||||
assert(generator != null);
|
assert(generator != null);
|
||||||
final DateTime candidateCompileTime = DateTime.now();
|
final DateTime candidateCompileTime = DateTime.now();
|
||||||
|
didUpdateFontManifest = false;
|
||||||
lastPackageConfig = packageConfig;
|
lastPackageConfig = packageConfig;
|
||||||
_widgetCacheOutputFile = _fileSystem.file('$dillOutputPath.incremental.dill.widget_cache');
|
_widgetCacheOutputFile = _fileSystem.file('$dillOutputPath.incremental.dill.widget_cache');
|
||||||
|
|
||||||
@ -644,6 +650,11 @@ class DevFS {
|
|||||||
if (deviceUri.path.startsWith(assetBuildDirPrefix)) {
|
if (deviceUri.path.startsWith(assetBuildDirPrefix)) {
|
||||||
archivePath = deviceUri.path.substring(assetBuildDirPrefix.length);
|
archivePath = deviceUri.path.substring(assetBuildDirPrefix.length);
|
||||||
}
|
}
|
||||||
|
// If the font manifest is updated, mark this as true so the hot runner
|
||||||
|
// can invoke a service extension to force the engine to reload fonts.
|
||||||
|
if (archivePath == _kFontManifest) {
|
||||||
|
didUpdateFontManifest = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (bundle.entryKinds[archivePath] == AssetKind.shader) {
|
if (bundle.entryKinds[archivePath] == AssetKind.shader) {
|
||||||
final Future<DevFSContent?> pending = shaderCompiler.recompileShader(content);
|
final Future<DevFSContent?> pending = shaderCompiler.recompileShader(content);
|
||||||
|
@ -677,6 +677,9 @@ class WebDevFS implements DevFS {
|
|||||||
@override
|
@override
|
||||||
bool hasSetAssetDirectory = false;
|
bool hasSetAssetDirectory = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool didUpdateFontManifest = false;
|
||||||
|
|
||||||
Future<DebugConnection>? _cachedExtensionFuture;
|
Future<DebugConnection>? _cachedExtensionFuture;
|
||||||
StreamSubscription<void>? _connectedApps;
|
StreamSubscription<void>? _connectedApps;
|
||||||
|
|
||||||
|
@ -1032,7 +1032,7 @@ class HotRunner extends ResidentRunner {
|
|||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
Future<void> evictDirtyAssets() async {
|
Future<void> evictDirtyAssets() async {
|
||||||
final List<Future<Map<String, dynamic>?>> futures = <Future<Map<String, dynamic>>>[];
|
final List<Future<void>> futures = <Future<void>>[];
|
||||||
for (final FlutterDevice? device in flutterDevices) {
|
for (final FlutterDevice? device in flutterDevices) {
|
||||||
if (device!.devFS!.assetPathsToEvict.isEmpty && device.devFS!.shaderPathsToEvict.isEmpty) {
|
if (device!.devFS!.assetPathsToEvict.isEmpty && device.devFS!.shaderPathsToEvict.isEmpty) {
|
||||||
continue;
|
continue;
|
||||||
@ -1060,6 +1060,14 @@ class HotRunner extends ResidentRunner {
|
|||||||
globals.printError('Application isolate not found for $device');
|
globals.printError('Application isolate not found for $device');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (device.devFS!.didUpdateFontManifest) {
|
||||||
|
futures.add(device.vmService!.reloadAssetFonts(
|
||||||
|
isolateId: views.first.uiIsolate!.id!,
|
||||||
|
viewId: views.first.id,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
for (final String assetPath in device.devFS!.assetPathsToEvict) {
|
for (final String assetPath in device.devFS!.assetPathsToEvict) {
|
||||||
futures.add(
|
futures.add(
|
||||||
device.vmService!
|
device.vmService!
|
||||||
@ -1081,7 +1089,7 @@ class HotRunner extends ResidentRunner {
|
|||||||
device.devFS!.assetPathsToEvict.clear();
|
device.devFS!.assetPathsToEvict.clear();
|
||||||
device.devFS!.shaderPathsToEvict.clear();
|
device.devFS!.shaderPathsToEvict.clear();
|
||||||
}
|
}
|
||||||
await Future.wait<Map<String, Object?>?>(futures);
|
await Future.wait<void>(futures);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -24,6 +24,7 @@ const String kListViewsMethod = '_flutter.listViews';
|
|||||||
const String kScreenshotSkpMethod = '_flutter.screenshotSkp';
|
const String kScreenshotSkpMethod = '_flutter.screenshotSkp';
|
||||||
const String kScreenshotMethod = '_flutter.screenshot';
|
const String kScreenshotMethod = '_flutter.screenshot';
|
||||||
const String kRenderFrameWithRasterStatsMethod = '_flutter.renderFrameWithRasterStats';
|
const String kRenderFrameWithRasterStatsMethod = '_flutter.renderFrameWithRasterStats';
|
||||||
|
const String kReloadAssetFonts = '_flutter.reloadAssetFonts';
|
||||||
|
|
||||||
/// The error response code from an unrecoverable compilation failure.
|
/// The error response code from an unrecoverable compilation failure.
|
||||||
const int kIsolateReloadBarred = 1005;
|
const int kIsolateReloadBarred = 1005;
|
||||||
@ -880,6 +881,20 @@ class FlutterVmService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tell the provided flutter view that the font manifest has been updated
|
||||||
|
/// and asset fonts should be reloaded.
|
||||||
|
Future<void> reloadAssetFonts({
|
||||||
|
required String isolateId,
|
||||||
|
required String viewId,
|
||||||
|
}) async {
|
||||||
|
await callMethodWrapper(
|
||||||
|
kReloadAssetFonts,
|
||||||
|
isolateId: isolateId, args: <String, Object?>{
|
||||||
|
'viewId': viewId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Waits for a signal from the VM service that [extensionName] is registered.
|
/// Waits for a signal from the VM service that [extensionName] is registered.
|
||||||
///
|
///
|
||||||
/// Looks at the list of loaded extensions for first Flutter view, as well as
|
/// Looks at the list of loaded extensions for first Flutter view, as well as
|
||||||
|
@ -643,6 +643,57 @@ void main() {
|
|||||||
FileSystem: () => fileSystem,
|
FileSystem: () => fileSystem,
|
||||||
ProcessManager: () => processManager,
|
ProcessManager: () => processManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testUsingContext('DevFS tracks when FontManifest is updated', () async {
|
||||||
|
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
||||||
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
||||||
|
httpAddress: Uri.parse('http://localhost'),
|
||||||
|
);
|
||||||
|
final BufferLogger logger = BufferLogger.test();
|
||||||
|
final DevFS devFS = DevFS(
|
||||||
|
fakeVmServiceHost.vmService,
|
||||||
|
'test',
|
||||||
|
fileSystem.currentDirectory,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
logger: logger,
|
||||||
|
osUtils: FakeOperatingSystemUtils(),
|
||||||
|
httpClient: FakeHttpClient.any(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await devFS.create();
|
||||||
|
|
||||||
|
expect(devFS.didUpdateFontManifest, false);
|
||||||
|
|
||||||
|
final FakeResidentCompiler residentCompiler = FakeResidentCompiler()
|
||||||
|
..onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
||||||
|
fileSystem.file('lib/foo.dill')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsBytesSync(<int>[1, 2, 3, 4, 5]);
|
||||||
|
return const CompilerOutput('lib/foo.dill', 0, <Uri>[]);
|
||||||
|
};
|
||||||
|
final FakeBundle bundle = FakeBundle()
|
||||||
|
..entries['FontManifest.json'] = DevFSByteContent(<int>[1, 2, 3, 4]);
|
||||||
|
|
||||||
|
final UpdateFSReport report = await devFS.update(
|
||||||
|
mainUri: Uri.parse('lib/main.dart'),
|
||||||
|
generator: residentCompiler,
|
||||||
|
dillOutputPath: 'lib/foo.dill',
|
||||||
|
pathToReload: 'lib/foo.txt.dill',
|
||||||
|
trackWidgetCreation: false,
|
||||||
|
invalidatedFiles: <Uri>[],
|
||||||
|
packageConfig: PackageConfig.empty,
|
||||||
|
shaderCompiler: const FakeShaderCompiler(),
|
||||||
|
bundle: bundle,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(report.success, true);
|
||||||
|
expect(devFS.shaderPathsToEvict, <String>{});
|
||||||
|
expect(devFS.assetPathsToEvict, <String>{'FontManifest.json'});
|
||||||
|
expect(devFS.didUpdateFontManifest, true);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => processManager,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2633,6 +2633,9 @@ class FakeDevFS extends Fake implements DevFS {
|
|||||||
@override
|
@override
|
||||||
Set<String> shaderPathsToEvict = <String>{};
|
Set<String> shaderPathsToEvict = <String>{};
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool didUpdateFontManifest = false;
|
||||||
|
|
||||||
UpdateFSReport nextUpdateReport = UpdateFSReport(success: true);
|
UpdateFSReport nextUpdateReport = UpdateFSReport(success: true);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user