diff --git a/assets/asset_manager.cc b/assets/asset_manager.cc index 60d169a31ebb2c2dd638aa1ce74b110e1920d6d6..0fbc28702e9f7876ad2f5a487f8923911ac57ac4 100644 --- a/assets/asset_manager.cc +++ b/assets/asset_manager.cc @@ -29,6 +29,10 @@ void AssetManager::PushBack(std::unique_ptr resolver) { resolvers_.push_back(std::move(resolver)); } +std::deque> AssetManager::TakeResolvers() { + return std::move(resolvers_); +} + // |AssetResolver| std::unique_ptr AssetManager::GetAsMapping( const std::string& asset_name) const { @@ -52,4 +56,9 @@ bool AssetManager::IsValid() const { return resolvers_.size() > 0; } +// |AssetResolver| +bool AssetManager::IsValidAfterAssetManagerChange() const { + return false; +} + } // namespace flutter diff --git a/assets/asset_manager.h b/assets/asset_manager.h index 2278742f50113e18b7c5e903600b60df0228883e..0a0f0ff170d4ce8001d5a51a6f8f6c91f5a6a80a 100644 --- a/assets/asset_manager.h +++ b/assets/asset_manager.h @@ -25,9 +25,14 @@ class AssetManager final : public AssetResolver { void PushBack(std::unique_ptr resolver); + std::deque> TakeResolvers(); + // |AssetResolver| bool IsValid() const override; + // |AssetResolver| + bool IsValidAfterAssetManagerChange() const override; + // |AssetResolver| std::unique_ptr GetAsMapping( const std::string& asset_name) const override; diff --git a/assets/asset_resolver.h b/assets/asset_resolver.h index b6cdb88b84ac02b77cad33ddaa6dcb38073201b4..8b3e323218939000726dc63c434fd931f78c82b9 100644 --- a/assets/asset_resolver.h +++ b/assets/asset_resolver.h @@ -21,6 +21,24 @@ class AssetResolver { virtual bool IsValid() const = 0; + //---------------------------------------------------------------------------- + /// @brief Certain asset resolvers are still valid after the asset + /// manager is replaced before a hot reload, or after a new run + /// configuration is created during a hot restart. By preserving + /// these resolvers and re-inserting them into the new resolver or + /// run configuration, the tooling can avoid needing to sync all + /// application assets through the Dart devFS upon connecting to + /// the VM Service. Besides improving the startup performance of + /// running a Flutter application, it also reduces the occurance + /// of tool failures due to repeated network flakes caused by + /// damaged cables or hereto unknown bugs in the Dart HTTP server + /// implementation. + /// + /// @return Returns whether this resolver is valid after the asset manager + /// or run configuration is updated. + /// + virtual bool IsValidAfterAssetManagerChange() const = 0; + [[nodiscard]] virtual std::unique_ptr GetAsMapping( const std::string& asset_name) const = 0; diff --git a/assets/directory_asset_bundle.cc b/assets/directory_asset_bundle.cc index 5ad7297313c996abb769253144671d312b3c695e..d15a7e7373c15b387c4249c4ecba0a45f16049c2 100644 --- a/assets/directory_asset_bundle.cc +++ b/assets/directory_asset_bundle.cc @@ -12,11 +12,14 @@ namespace flutter { -DirectoryAssetBundle::DirectoryAssetBundle(fml::UniqueFD descriptor) +DirectoryAssetBundle::DirectoryAssetBundle( + fml::UniqueFD descriptor, + bool is_valid_after_asset_manager_change) : descriptor_(std::move(descriptor)) { if (!fml::IsDirectory(descriptor_)) { return; } + is_valid_after_asset_manager_change_ = is_valid_after_asset_manager_change; is_valid_ = true; } @@ -27,6 +30,11 @@ bool DirectoryAssetBundle::IsValid() const { return is_valid_; } +// |AssetResolver| +bool DirectoryAssetBundle::IsValidAfterAssetManagerChange() const { + return is_valid_after_asset_manager_change_; +} + // |AssetResolver| std::unique_ptr DirectoryAssetBundle::GetAsMapping( const std::string& asset_name) const { diff --git a/assets/directory_asset_bundle.h b/assets/directory_asset_bundle.h index 0a0a94c7aba15aebffdadd6493e8b75729083eee..49b02cdd27c71fd8c220ccb70175965d9cf74e60 100644 --- a/assets/directory_asset_bundle.h +++ b/assets/directory_asset_bundle.h @@ -14,17 +14,22 @@ namespace flutter { class DirectoryAssetBundle : public AssetResolver { public: - explicit DirectoryAssetBundle(fml::UniqueFD descriptor); + DirectoryAssetBundle(fml::UniqueFD descriptor, + bool is_valid_after_asset_manager_change); ~DirectoryAssetBundle() override; private: const fml::UniqueFD descriptor_; bool is_valid_ = false; + bool is_valid_after_asset_manager_change_ = false; // |AssetResolver| bool IsValid() const override; + // |AssetResolver| + bool IsValidAfterAssetManagerChange() const override; + // |AssetResolver| std::unique_ptr GetAsMapping( const std::string& asset_name) const override; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index d2384c03579cfb94065e8bcb8d6ce1a502d02323..348e69a00a9aad860d2bb586d1b6a898bc1e27e2 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -106,6 +106,10 @@ void Engine::SetupDefaultFontManager() { font_collection_.SetupDefaultFontManager(); } +std::shared_ptr Engine::GetAssetManager() { + return asset_manager_; +} + bool Engine::UpdateAssetManager( std::shared_ptr new_asset_manager) { if (asset_manager_ == new_asset_manager) { diff --git a/shell/common/engine.h b/shell/common/engine.h index b8d98aa766291c3dd28bb45d6a661bfc05308dfe..c5bbfc42c9c74a57fc8992bea9466c58285748e1 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -734,6 +734,9 @@ class Engine final : public RuntimeDelegate, // |RuntimeDelegate| FontCollection& GetFontCollection() override; + // Return the asset manager associated with the current engine, or nullptr. + std::shared_ptr GetAssetManager(); + // |PointerDataDispatcher::Delegate| void DoDispatchPacket(std::unique_ptr packet, uint64_t trace_flow_id) override; diff --git a/shell/common/persistent_cache_unittests.cc b/shell/common/persistent_cache_unittests.cc index a9ecfb1b39adb1bb23f80b9d8aa138cfc4c108c8..77c5a8a9ddf52159d6cf76dfa0c52f243ede1748 100644 --- a/shell/common/persistent_cache_unittests.cc +++ b/shell/common/persistent_cache_unittests.cc @@ -185,9 +185,10 @@ TEST_F(ShellTest, CanLoadSkSLsFromAsset) { ResetAssetManager(); auto asset_manager = std::make_shared(); RunConfiguration config(nullptr, asset_manager); - asset_manager->PushBack( - std::make_unique(fml::OpenDirectory( - asset_dir.path().c_str(), false, fml::FilePermission::kRead))); + asset_manager->PushBack(std::make_unique( + fml::OpenDirectory(asset_dir.path().c_str(), false, + fml::FilePermission::kRead), + false)); CheckTwoSkSLsAreLoaded(); // 3rd, test the content of the SkSLs in the asset. diff --git a/shell/common/run_configuration.cc b/shell/common/run_configuration.cc index 8f0966b6bc896ddc64e659b7403d4a5525ec4a39..1b32d5c295fbd50714fc12020855e75fbb3f90c7 100644 --- a/shell/common/run_configuration.cc +++ b/shell/common/run_configuration.cc @@ -21,12 +21,13 @@ RunConfiguration RunConfiguration::InferFromSettings( if (fml::UniqueFD::traits_type::IsValid(settings.assets_dir)) { asset_manager->PushBack(std::make_unique( - fml::Duplicate(settings.assets_dir))); + fml::Duplicate(settings.assets_dir), true)); } - asset_manager->PushBack( - std::make_unique(fml::OpenDirectory( - settings.assets_path.c_str(), false, fml::FilePermission::kRead))); + asset_manager->PushBack(std::make_unique( + fml::OpenDirectory(settings.assets_path.c_str(), false, + fml::FilePermission::kRead), + true)); return {IsolateConfiguration::InferFromSettings(settings, asset_manager, io_worker), diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 2da9e9786e38569cd2afb903095c779a664496aa..c250ca25a8552776a6e1b51cac4fbed507e3babf 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1401,10 +1401,21 @@ bool Shell::OnServiceProtocolRunInView( configuration.SetEntrypointAndLibrary(engine_->GetLastEntrypoint(), engine_->GetLastEntrypointLibrary()); - configuration.AddAssetResolver( - std::make_unique(fml::OpenDirectory( - asset_directory_path.c_str(), false, fml::FilePermission::kRead))); - configuration.AddAssetResolver(RestoreOriginalAssetResolver()); + configuration.AddAssetResolver(std::make_unique( + fml::OpenDirectory(asset_directory_path.c_str(), false, + fml::FilePermission::kRead), + false)); + + // Preserve any original asset resolvers to avoid syncing unchanged assets + // over the DevFS connection. + auto old_asset_manager = engine_->GetAssetManager(); + if (old_asset_manager != nullptr) { + for (auto& old_resolver : old_asset_manager->TakeResolvers()) { + if (old_resolver->IsValidAfterAssetManagerChange()) { + configuration.AddAssetResolver(std::move(old_resolver)); + } + } + } auto& allocator = response->GetAllocator(); response->SetObject(); @@ -1517,10 +1528,21 @@ bool Shell::OnServiceProtocolSetAssetBundlePath( auto asset_manager = std::make_shared(); - asset_manager->PushFront(RestoreOriginalAssetResolver()); asset_manager->PushFront(std::make_unique( fml::OpenDirectory(params.at("assetDirectory").data(), false, - fml::FilePermission::kRead))); + fml::FilePermission::kRead), + false)); + + // Preserve any original asset resolvers to avoid syncing unchanged assets + // over the DevFS connection. + auto old_asset_manager = engine_->GetAssetManager(); + if (old_asset_manager != nullptr) { + for (auto& old_resolver : old_asset_manager->TakeResolvers()) { + if (old_resolver->IsValidAfterAssetManagerChange()) { + asset_manager->PushBack(std::move(old_resolver)); + } + } + } if (engine_->UpdateAssetManager(std::move(asset_manager))) { response->AddMember("type", "Success", allocator); @@ -1624,16 +1646,4 @@ void Shell::OnDisplayUpdates(DisplayUpdateType update_type, display_manager_->HandleDisplayUpdates(update_type, displays); } -// Add the original asset directory to the resolvers so that unmodified assets -// bundled with the application specific format (APK, IPA) can be used without -// syncing to the Dart devFS. -std::unique_ptr Shell::RestoreOriginalAssetResolver() { - if (fml::UniqueFD::traits_type::IsValid(settings_.assets_dir)) { - return std::make_unique( - fml::Duplicate(settings_.assets_dir)); - } - return std::make_unique(fml::OpenDirectory( - settings_.assets_path.c_str(), false, fml::FilePermission::kRead)); -}; - } // namespace flutter diff --git a/shell/platform/android/apk_asset_provider.cc b/shell/platform/android/apk_asset_provider.cc index 49af5ffa0182a6915f6732b976ea8208701cf5f1..73a4a26bd1d2dab27341bf2e031ab0e8d05663ad 100644 --- a/shell/platform/android/apk_asset_provider.cc +++ b/shell/platform/android/apk_asset_provider.cc @@ -27,6 +27,10 @@ bool APKAssetProvider::IsValid() const { return true; } +bool APKAssetProvider::IsValidAfterAssetManagerChange() const { + return true; +} + class APKAssetMapping : public fml::Mapping { public: APKAssetMapping(AAsset* asset) : asset_(asset) {} diff --git a/shell/platform/android/apk_asset_provider.h b/shell/platform/android/apk_asset_provider.h index 6ff1e8da7b776869f0d3ab6cc32f26464fc6e256..f457b6c4ecc167d3b73be7982c66817c03bdd954 100644 --- a/shell/platform/android/apk_asset_provider.h +++ b/shell/platform/android/apk_asset_provider.h @@ -29,6 +29,9 @@ class APKAssetProvider final : public AssetResolver { // |flutter::AssetResolver| bool IsValid() const override; + // |flutter::AssetResolver| + bool IsValidAfterAssetManagerChange() const override; + // |flutter::AssetResolver| std::unique_ptr GetAsMapping( const std::string& asset_name) const override; diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index 0f0264ce9ebdbf52fb7a19704d239eb5e5a91a91..464b51c147fdb30c9ac3bfd1fa3423496c9fc49e 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -192,10 +192,11 @@ int RunTester(const flutter::Settings& settings, auto asset_manager = std::make_shared(); asset_manager->PushBack(std::make_unique( - fml::Duplicate(settings.assets_dir))); - asset_manager->PushBack( - std::make_unique(fml::OpenDirectory( - settings.assets_path.c_str(), false, fml::FilePermission::kRead))); + fml::Duplicate(settings.assets_dir), true)); + asset_manager->PushBack(std::make_unique( + fml::OpenDirectory(settings.assets_path.c_str(), false, + fml::FilePermission::kRead), + true)); RunConfiguration run_configuration(std::move(isolate_configuration), std::move(asset_manager));