[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 'vmservice.dart';
|
||||
|
||||
const String _kFontManifest = 'FontManifest.json';
|
||||
|
||||
class DevFSConfig {
|
||||
/// Should DevFS assume that symlink targets are stable?
|
||||
bool cacheSymlinks = false;
|
||||
@ -485,6 +487,9 @@ class DevFS {
|
||||
// A flag to indicate whether we have called `setAssetDirectory` on the target device.
|
||||
bool hasSetAssetDirectory = false;
|
||||
|
||||
/// Whether the font manifest was uploaded during [update].
|
||||
bool didUpdateFontManifest = false;
|
||||
|
||||
List<Uri> sources = <Uri>[];
|
||||
DateTime? lastCompiled;
|
||||
DateTime? _previousCompiled;
|
||||
@ -589,6 +594,7 @@ class DevFS {
|
||||
assert(trackWidgetCreation != null);
|
||||
assert(generator != null);
|
||||
final DateTime candidateCompileTime = DateTime.now();
|
||||
didUpdateFontManifest = false;
|
||||
lastPackageConfig = packageConfig;
|
||||
_widgetCacheOutputFile = _fileSystem.file('$dillOutputPath.incremental.dill.widget_cache');
|
||||
|
||||
@ -644,6 +650,11 @@ class DevFS {
|
||||
if (deviceUri.path.startsWith(assetBuildDirPrefix)) {
|
||||
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) {
|
||||
final Future<DevFSContent?> pending = shaderCompiler.recompileShader(content);
|
||||
|
@ -677,6 +677,9 @@ class WebDevFS implements DevFS {
|
||||
@override
|
||||
bool hasSetAssetDirectory = false;
|
||||
|
||||
@override
|
||||
bool didUpdateFontManifest = false;
|
||||
|
||||
Future<DebugConnection>? _cachedExtensionFuture;
|
||||
StreamSubscription<void>? _connectedApps;
|
||||
|
||||
|
@ -1032,7 +1032,7 @@ class HotRunner extends ResidentRunner {
|
||||
|
||||
@visibleForTesting
|
||||
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) {
|
||||
if (device!.devFS!.assetPathsToEvict.isEmpty && device.devFS!.shaderPathsToEvict.isEmpty) {
|
||||
continue;
|
||||
@ -1060,6 +1060,14 @@ class HotRunner extends ResidentRunner {
|
||||
globals.printError('Application isolate not found for $device');
|
||||
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) {
|
||||
futures.add(
|
||||
device.vmService!
|
||||
@ -1081,7 +1089,7 @@ class HotRunner extends ResidentRunner {
|
||||
device.devFS!.assetPathsToEvict.clear();
|
||||
device.devFS!.shaderPathsToEvict.clear();
|
||||
}
|
||||
await Future.wait<Map<String, Object?>?>(futures);
|
||||
await Future.wait<void>(futures);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -24,6 +24,7 @@ const String kListViewsMethod = '_flutter.listViews';
|
||||
const String kScreenshotSkpMethod = '_flutter.screenshotSkp';
|
||||
const String kScreenshotMethod = '_flutter.screenshot';
|
||||
const String kRenderFrameWithRasterStatsMethod = '_flutter.renderFrameWithRasterStats';
|
||||
const String kReloadAssetFonts = '_flutter.reloadAssetFonts';
|
||||
|
||||
/// The error response code from an unrecoverable compilation failure.
|
||||
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.
|
||||
///
|
||||
/// Looks at the list of loaded extensions for first Flutter view, as well as
|
||||
|
@ -643,6 +643,57 @@ void main() {
|
||||
FileSystem: () => fileSystem,
|
||||
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
|
||||
Set<String> shaderPathsToEvict = <String>{};
|
||||
|
||||
@override
|
||||
bool didUpdateFontManifest = false;
|
||||
|
||||
UpdateFSReport nextUpdateReport = UpdateFSReport(success: true);
|
||||
|
||||
@override
|
||||
|
Loading…
x
Reference in New Issue
Block a user