在现代浏览器开发中,资源管理(images、fonts、strings 等)是 UI 渲染的重要组成部分。Chromium 提供了 ResourceBundle
作为统一的资源加载与缓存中心,但在实际应用中,很多定制化需求,例如主题皮肤切换、多语言资源覆盖、定制字体等,Chromium 的原生机制无法直接满足。本文将深入解析 定制 ResourceBundle 的实现 ,并结合 DuiLib 思想,探讨其在 Chromium 架构下的落地实践。
本文将从 What(是什么) 、How(怎么做) 、Why(为什么这么做) 三个角度展开,结合代码实例解析实现逻辑与设计理念。
What:ResourceBundle 是什么?为什么需要定制
Chromium 原生 ResourceBundle
Chromium 的 ResourceBundle
位于 ui/base/resource/resource_bundle.h
,它的核心作用是统一管理应用所需的资源:
-
图片(
gfx::Image
,gfx::ImageSkia
) -
字符串(国际化、
std::u16string
) -
字体(
gfx::FontList
) -
二进制数据资源(Data Pack)
-
压缩资源(GZip、Brotli)
核心类设计如下(简化):
class ResourceBundle { public: enum FontStyle { SmallFont, BaseFont, BoldFont, MediumFont, MediumBoldFont, LargeFont }; struct FontDetails { std::string typeface; int size_delta; gfx::Font::Weight weight; }; static ResourceBundle& GetSharedInstance(); gfx::Image& GetImageNamed(int resource_id); std::u16string GetLocalizedString(int resource_id); const gfx::FontList& GetFontList(FontStyle style); };
特点:
-
单例管理 :全局共享实例
GetSharedInstance()
。 -
按需加载:图片和字符串按需加载,避免重复读取。
-
缓存机制:加载的资源会被缓存,提高性能。
-
可扩展 Delegate :通过
ResourceBundle::Delegate
可实现自定义资源获取。
定制需求
尽管 Chromium 提供了丰富的接口,但在实际项目中,开发者常有如下需求:
-
主题皮肤切换:不同的界面风格(亮色/暗色、专业版/普通版)。
-
多语言资源覆盖:支持动态替换字符串、字体及图片。
-
本地资源打包:支持 zip 或自定义打包格式加载资源。
-
DuiLib 式 UI 思想:快速解析 XML/Json 配置,实现控件属性绑定、动态换肤。
所以定制 ResourceBundle 就应运而生。它通过继承 Chromium 的 ResourceBundle::Delegate
,覆盖资源加载逻辑,实现灵活可扩展的皮肤和多语言机制。
How:定制 ResourceBundle 的实现逻辑
1. Delegate 定制
在 Chromium 中,ResourceBundle
通过 Delegate
获取资源。定制 ResourceBundle 主要就是实现这个接口:
class BrowserTheme360ResourceBundle : public ui::ResourceBundle::Delegate { public: BrowserTheme360ResourceBundle(); ~BrowserTheme360ResourceBundle() override; // 文件路径 base::FilePath GetPathForResourcePack(const base::FilePath& pack_path, ui::ResourceScaleFactor scale_factor) override; base::FilePath GetPathForLocalePack(const base::FilePath& pack_path, const std::string& locale) override; // 图片资源 gfx::Image GetImageNamed(int resource_id) override; gfx::Image GetImageNamed(const std::string& name) override; gfx::Image GetNativeImageNamed(int resource_id) override; // 字符串资源 bool GetLocalizedString(int message_id, std::u16string* value) override; // 字体 std::unique_ptr<gfx::Font> GetFont(int style) override; // 自定义资源加载 std::string_view GetRawDataResource(const std::string& name) override; void ChangeSkin(const std::string& name, bool force = false) override; };
核心思路:
-
将图片、字符串、字体统一管理到一个自定义的 ZIP 或 JSON 配置包中。
-
通过
GetImageNamed
、GetLocalizedString
等接口,从自定义包中读取资源。 -
支持动态切换主题,通过
ChangeSkin
更新内存中的资源映射。
2. 资源管理机制
a) 图片资源缓存
定制实现中使用类似 DuiLib 的机制,对图片进行缓存管理:
std::map<std::string, gfx::Image> name_images_; gfx::Image BrowserTheme360ResourceBundle::GetImageNamed(const std::string& name) { auto it = name_images_.find(name); if (it != name_images_.end()) { return it->second; } // 从 zip 包加载 std::string data; if (GetZipResRawData(name, data)) { gfx::Image img = LoadImageFromBuffer(data); name_images_[name] = img; return img; } return GetEmptyImage(); }
特点:
-
按需加载:未访问的资源不会占用内存。
-
缓存机制:多次访问同一资源直接返回缓存。
-
容错机制:找不到资源时返回空白图片。
b) 字符串资源管理
通过 map_strings_
和 map_config_
存储资源 ID 与字符串映射,支持动态覆盖:
std::map<int, std::u16string> map_strings_; bool BrowserTheme360ResourceBundle::GetLocalizedString(int message_id, std::u16string* value) { auto it = map_strings_.find(message_id); if (it != map_strings_.end()) { *value = it->second; return true; } return false; }
-
Why :在主题切换或语言切换时,可以动态更新
map_strings_
。 -
What:统一管理所有字符串资源,避免散落在代码中。
c) Font 管理
类似地,字体通过 map_fonts_
和 theme_fonts_
管理,支持动态加载与主题切换:
std::map<int, gfx::FontList*> map_fonts_; std::unique_ptr<gfx::Font> BrowserTheme360ResourceBundle::GetFont(int style) { auto it = map_fonts_.find(style); if (it != map_fonts_.end()) return std::make_unique<gfx::Font>(*(it->second)); return std::make_unique<gfx::Font>(GetDefaultFont(style)); }
-
Why:支持不同主题下字体大小、加粗等属性调整。
-
How:通过 FontList 缓存减少重复创建。
3. 自定义 ZIP 资源包
定制实现支持从 ZIP 包读取资源,实现 DuiLib 风格的资源集中管理:
bool BrowserTheme360ResourceBundle::GetZipResRawData(const std::string& name, std::string& sp) { ResZipType* zip = &m_resZip; // 当前皮肤 ZIP if (zip->IsExist(name)) { zip->GetFile(name, sp); return true; } return false; }
-
DuiLib 思想借鉴:
-
DuiLib 使用 XML/ZIP 存储 UI 资源。
-
浏览器定制实现中,将皮肤资源打包成 ZIP,统一加载。
-
-
Why:集中管理资源,方便换肤、更新和压缩。
4. 主题切换机制
定制 ResourceBundle 支持动态换肤:
void BrowserTheme360ResourceBundle::ChangeSkin(const std::string& name, bool force) { if (cur_skin_name_ == name && !force) return; cur_skin_name_ = name; // 清理缓存 name_images_.clear(); map_strings_.clear(); map_fonts_.clear(); // 加载新皮肤配置 LoadDefaultSkinConfig(); }
-
What:动态替换所有资源映射,实现界面风格变化。
-
How:清空旧缓存,加载新 ZIP / JSON 配置。
-
Why:用户体验需求,允许实时切换皮肤而无需重启浏览器。
Why:为什么要这样实现?
1. 提升灵活性
原生 Chromium 的 ResourceBundle 对于多主题、多语言支持有限,无法动态加载 ZIP 包或集中管理资源。通过定制:
-
可以动态加载任意皮肤资源包。
-
可以在运行时修改字符串和字体。
-
可以按需加载和缓存资源,节约内存。
2. 借鉴 DuiLib 思想
DuiLib 的核心思想是:
-
XML/ZIP 统一管理资源和控件属性。
-
按需加载,只在控件创建或访问时读取资源。
-
主题和皮肤可热替换,所有控件可自动刷新。
在 Chromium 中:
-
ZIP 替代 GRIT 生成的静态 Pak 文件,便于扩展和换肤。
-
字符串、字体、图片统一映射,类似 DuiLib 的控件属性系统。
-
热切换皮肤时,只需刷新缓存映射,无需重新启动。
3. 提升可维护性
-
集中管理资源,减少散落在代码中的硬编码路径或 ID。
-
支持多语言覆盖,便于国际化开发。
-
支持动态主题切换,便于产品定制和测试。
5. 总结与实践价值
本文基于 Chromium ResourceBundle 的原生机制,结合 DuiLib 的资源管理思想,实现了一套 定制化资源加载与缓存方案:
-
What:统一管理图片、字符串、字体资源,支持压缩和多语言。
-
How :通过继承
ResourceBundle::Delegate
,结合 ZIP / JSON 资源包,实现按需加载、缓存、动态换肤。 -
Why:提升灵活性、可维护性和用户体验,同时借鉴成熟 UI 框架的设计思想。
实践经验:
-
使用缓存减少重复解码,提高性能。
-
ZIP 包集中管理资源,提高可扩展性。
-
动态换肤需确保所有缓存资源同步刷新。
-
国际化资源统一映射,支持动态覆盖。
-
DuiLib 风格思想在现代浏览器架构中可行且高效。
附:关键代码片段汇总
// 获取图片资源 gfx::Image BrowserTheme360ResourceBundle::GetImageNamed(const std::string& name) { auto it = name_images_.find(name); if (it != name_images_.end()) return it->second; std::string data; if (GetZipResRawData(name, data)) { gfx::Image img = LoadImageFromBuffer(data); name_images_[name] = img; return img; } return GetEmptyImage(); } // 动态切换皮肤 void BrowserTheme360ResourceBundle::ChangeSkin(const std::string& name, bool force) { if (cur_skin_name_ == name && !force) return; cur_skin_name_ = name; name_images_.clear(); map_strings_.clear(); map_fonts_.clear(); LoadDefaultSkinConfig(); } // 获取字符串资源 bool BrowserTheme360ResourceBundle::GetLocalizedString(int message_id, std::u16string* value) { auto it = map_strings_.find(message_id); if (it != map_strings_.end()) { *value = it->second; return true; } return false; }
这套定制 ResourceBundle 实现充分体现了 现代浏览器 UI 模块化设计、灵活性与可扩展性 的理念。通过结合 DuiLib 的思想,可以在 Chromium 架构下实现高性能、可维护且易扩展的主题与资源管理方案。