Skip to content

Commit

Permalink
Preserve specified AssetResolvers when performing a hot restart or up…
Browse files Browse the repository at this point in the history
…dating the asset directory (flutter#21611)

Follow up from flutter#21436 . That PR works for all embeddings except for Android, which creates a special JNI AssetResolver. Since the shell cannot recreate this resolver, update the logic to preserve existing resolvers instead.
  • Loading branch information
jonahwilliams authored Oct 8, 2020
1 parent 4c6f2ad commit 15c5874
Show file tree
Hide file tree
Showing 13 changed files with 103 additions and 31 deletions.
9 changes: 9 additions & 0 deletions assets/asset_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ void AssetManager::PushBack(std::unique_ptr<AssetResolver> resolver) {
resolvers_.push_back(std::move(resolver));
}

std::deque<std::unique_ptr<AssetResolver>> AssetManager::TakeResolvers() {
return std::move(resolvers_);
}

// |AssetResolver|
std::unique_ptr<fml::Mapping> AssetManager::GetAsMapping(
const std::string& asset_name) const {
Expand All @@ -52,4 +56,9 @@ bool AssetManager::IsValid() const {
return resolvers_.size() > 0;
}

// |AssetResolver|
bool AssetManager::IsValidAfterAssetManagerChange() const {
return false;
}

} // namespace flutter
5 changes: 5 additions & 0 deletions assets/asset_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ class AssetManager final : public AssetResolver {

void PushBack(std::unique_ptr<AssetResolver> resolver);

std::deque<std::unique_ptr<AssetResolver>> TakeResolvers();

// |AssetResolver|
bool IsValid() const override;

// |AssetResolver|
bool IsValidAfterAssetManagerChange() const override;

// |AssetResolver|
std::unique_ptr<fml::Mapping> GetAsMapping(
const std::string& asset_name) const override;
Expand Down
18 changes: 18 additions & 0 deletions assets/asset_resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<fml::Mapping> GetAsMapping(
const std::string& asset_name) const = 0;

Expand Down
10 changes: 9 additions & 1 deletion assets/directory_asset_bundle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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<fml::Mapping> DirectoryAssetBundle::GetAsMapping(
const std::string& asset_name) const {
Expand Down
7 changes: 6 additions & 1 deletion assets/directory_asset_bundle.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<fml::Mapping> GetAsMapping(
const std::string& asset_name) const override;
Expand Down
4 changes: 4 additions & 0 deletions shell/common/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ void Engine::SetupDefaultFontManager() {
font_collection_.SetupDefaultFontManager();
}

std::shared_ptr<AssetManager> Engine::GetAssetManager() {
return asset_manager_;
}

bool Engine::UpdateAssetManager(
std::shared_ptr<AssetManager> new_asset_manager) {
if (asset_manager_ == new_asset_manager) {
Expand Down
3 changes: 3 additions & 0 deletions shell/common/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<AssetManager> GetAssetManager();

// |PointerDataDispatcher::Delegate|
void DoDispatchPacket(std::unique_ptr<PointerDataPacket> packet,
uint64_t trace_flow_id) override;
Expand Down
7 changes: 4 additions & 3 deletions shell/common/persistent_cache_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,10 @@ TEST_F(ShellTest, CanLoadSkSLsFromAsset) {
ResetAssetManager();
auto asset_manager = std::make_shared<AssetManager>();
RunConfiguration config(nullptr, asset_manager);
asset_manager->PushBack(
std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory(
asset_dir.path().c_str(), false, fml::FilePermission::kRead)));
asset_manager->PushBack(std::make_unique<DirectoryAssetBundle>(
fml::OpenDirectory(asset_dir.path().c_str(), false,
fml::FilePermission::kRead),
false));
CheckTwoSkSLsAreLoaded();

// 3rd, test the content of the SkSLs in the asset.
Expand Down
9 changes: 5 additions & 4 deletions shell/common/run_configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ RunConfiguration RunConfiguration::InferFromSettings(

if (fml::UniqueFD::traits_type::IsValid(settings.assets_dir)) {
asset_manager->PushBack(std::make_unique<DirectoryAssetBundle>(
fml::Duplicate(settings.assets_dir)));
fml::Duplicate(settings.assets_dir), true));
}

asset_manager->PushBack(
std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory(
settings.assets_path.c_str(), false, fml::FilePermission::kRead)));
asset_manager->PushBack(std::make_unique<DirectoryAssetBundle>(
fml::OpenDirectory(settings.assets_path.c_str(), false,
fml::FilePermission::kRead),
true));

return {IsolateConfiguration::InferFromSettings(settings, asset_manager,
io_worker),
Expand Down
46 changes: 28 additions & 18 deletions shell/common/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1401,10 +1401,21 @@ bool Shell::OnServiceProtocolRunInView(
configuration.SetEntrypointAndLibrary(engine_->GetLastEntrypoint(),
engine_->GetLastEntrypointLibrary());

configuration.AddAssetResolver(
std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory(
asset_directory_path.c_str(), false, fml::FilePermission::kRead)));
configuration.AddAssetResolver(RestoreOriginalAssetResolver());
configuration.AddAssetResolver(std::make_unique<DirectoryAssetBundle>(
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();
Expand Down Expand Up @@ -1517,10 +1528,21 @@ bool Shell::OnServiceProtocolSetAssetBundlePath(

auto asset_manager = std::make_shared<AssetManager>();

asset_manager->PushFront(RestoreOriginalAssetResolver());
asset_manager->PushFront(std::make_unique<DirectoryAssetBundle>(
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);
Expand Down Expand Up @@ -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<DirectoryAssetBundle> Shell::RestoreOriginalAssetResolver() {
if (fml::UniqueFD::traits_type::IsValid(settings_.assets_dir)) {
return std::make_unique<DirectoryAssetBundle>(
fml::Duplicate(settings_.assets_dir));
}
return std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory(
settings_.assets_path.c_str(), false, fml::FilePermission::kRead));
};

} // namespace flutter
4 changes: 4 additions & 0 deletions shell/platform/android/apk_asset_provider.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down
3 changes: 3 additions & 0 deletions shell/platform/android/apk_asset_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<fml::Mapping> GetAsMapping(
const std::string& asset_name) const override;
Expand Down
9 changes: 5 additions & 4 deletions shell/testing/tester_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,11 @@ int RunTester(const flutter::Settings& settings,

auto asset_manager = std::make_shared<flutter::AssetManager>();
asset_manager->PushBack(std::make_unique<flutter::DirectoryAssetBundle>(
fml::Duplicate(settings.assets_dir)));
asset_manager->PushBack(
std::make_unique<flutter::DirectoryAssetBundle>(fml::OpenDirectory(
settings.assets_path.c_str(), false, fml::FilePermission::kRead)));
fml::Duplicate(settings.assets_dir), true));
asset_manager->PushBack(std::make_unique<flutter::DirectoryAssetBundle>(
fml::OpenDirectory(settings.assets_path.c_str(), false,
fml::FilePermission::kRead),
true));

RunConfiguration run_configuration(std::move(isolate_configuration),
std::move(asset_manager));
Expand Down

0 comments on commit 15c5874

Please sign in to comment.