AndroidChromium源码编译全攻略:从解压到APK生成与最小定制
简介这份Android Chromium浏览器源码面向Android应用开发者与浏览器内核研究者是剖析Chrome在移动端运行机制的一手材料。压缩包为rar格式大小约30.91MB轻量易下载目前已有673人学习。通过阅读源码可重点研习V8引擎的JIT编译与垃圾回收、Blink渲染引擎的HTML/CSS解析与绘制流程、多进程架构下的IPC通信、HTTP/2网络请求与缓存策略以及Chromium与Android系统的深度集成方式。此外预加载、预渲染、内存池等性能优化手段和安全沙盒机制也完整呈现能帮助开发者掌握从页面加载到渲染绘制的完整链路为开发高性能、高稳定性的移动应用提供直接参考。1. AndroidChromium 源码是什么为什么值得花时间解压它一个叫android应用源码---浏览器源码(AndroidChromium).rar的压缩包解压之后的体积通常会让人怀疑自己是不是装错了东西。它不是一个能用 Android Studio 直接打开的普通 App 工程而是 Chromium 项目针对 Android 平台维护的完整移植版源码树浏览器 UI、渲染引擎、网络栈、GPU 进程、安全沙箱、V8 JavaScript 引擎全部都在里面。这意味着两件事第一它的构建方式与绝大多数 Android 应用完全不同入口是 GN 加 Ninja而不是 Gradle第二你能在这套源码里看到的不只是一个“浏览器 App”而是一种多进程架构在产品代码里的落地形态。如果你正在做 Web 内核封装、系统 WebView 定制或者想研究 Android 平台上 Java 与 C 的 JNI 协作这套源码比任何二次封装的开源浏览器都更值得花时间读进去。2. AndroidChromium 源码的准备从压缩包到可编译目录2.1 先看清压缩包里的工程是哪种形态拿到压缩包后第一件事不是写代码而是确认它的形态。Chromium 源码体积太大打包者在打包时通常会做两类处理一类是包含.gclient配置文件但没拉全依赖的仓库根目录这类解压后还需要走一次完整的gclient sync另一类是已经 sync 完毕、只把src/目录压缩起来的快照这类省去了最耗时的代码同步但编译所需工具链仍然要单独准备。区分方式很简单在解压目录下执行ls -la如果能看到src/目录而且里面已经有BUILD.gn说明依赖基本在如果只有.gclient和若干脚本说明它只是仓库骨架。我一般会把这两类包当成不同流程处理前者重点调gclient后者重点跑 hooks。无论哪种都不要试图用 Android Studio 的 “Open an existing project” 直接打开这里没有settings.gradle它认识不了这套构建组织方式。2.2 Chromium for Android 源码目录地图面向 Android 平台看这个源码树最先要记住的不是全部目录而是下面这七块核心区域。它们各自管一件事修改时定位问题也用得上。目录主要职责什么时候需要动它base/文件、线程、字符串、内存等基础设施需要替换系统调用或加全局日志时net/网络栈HTTP/2、QUIC、代理、Cookie 管理调整网络协议行为或截获请求时content/public浏览器进程与渲染进程之间的核心接口把内核能力暴露给上层封装时chrome/android浏览器 UI 的 Java 与 Native 实现改界面、改入口、加浏览器功能时content/public/androidAndroid 侧 JNI 入口与辅助工具需要从 Java 调原生能力时components/*/android各类功能模块的 Android 分支单独换下载、书签、自动填充模块时android_webview/以系统 WebView 形态封装整个内核要做 WebView 而不仅是浏览器时这些目录的层级关系是base垫底net和content在它上面chrome又依赖content。Android 专用代码不会单独游离在整个框架之外而是以android目录或*_java目标的形式插进对应模块阅读时要有“同一个模块同时存在 Java、C、JNI 三部分”的心理预期。2.3 depot_tools 是这套源码的构建入口depot_tools是 Chromium 体系的工具集对外最重要的三个可执行文件是gclient、gn和ninja。gclient负责管理多个 git 仓库之间的依赖关系gn负责生成构建描述ninja负责真正执行编译。AndroidChromium 源码里不会自带 depot_tools需要单独克隆。cd androidchromium git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git export PATH$PWD/depot_tools:$PATH export DEPOT_TOOLS_UPDATE0 # 若压缩包带 .gclient只做依赖同步 # 若 src 已在包内这一行也会补充遗漏的嵌套仓库 gclient sync --nohooks这段命令里真正值得解释的是环境变量和--nohooks。DEPOT_TOOLS_UPDATE0会让 depot_tools 停掉自动更新没有这一行每执行一次工具都会先尝试把自身切到最新版本而新版工具对 Python 和系统库的要求可能与你当前环境冲突这是很常见的“昨天还能跑今天莫名其妙报错”的原因。gclient sync根据根目录下的.gclient文件把 Chromium 依赖的几百个仓库同步到各自固定 revision--nohooks表示“只拉代码不执行后续的下载和脚本操作”这样可以把同步失败与 hooks 失败分隔开排错时少一半干扰。2.4 系统依赖与 hooks真正耗时的长尾代码同步完成只是第一步。Chromium for Android 编译需要 Clang 工具链、Android NDK、sysroot以及若干辅助包这些由 DEPS 文件定义、在 hooks 阶段被拉取。直接把这一步跳过的后果是gn gen报一堆“missing xxx”错误而且错误信息不直观。build/install-build-deps-android.sh # 安装系统级编译依赖需要 sudo gclient runhooks # 下载 LLVM、sysroot 等构建工具链install-build-deps-android.sh是 Chromium 官方提供的脚本会自动识别发行版并安装编译需要的系统库如果你在一个新容器里构建它比手工逐条apt install可靠得多。gclient runhooks幂等中断后重跑就行不需要回滚之前同步的仓库。这里有一个容易被忽略的点runhooks下载的内容量和代码本身一样大建议在磁盘余量充足的分区上执行通常预留 80 GB 以上再动手。磁盘满导致的ERR_FILE_NOT_FOUND很容易被误判成网络问题实际上只要删掉部分缓存再重试即可。3. AndroidChromium 的源码架构从 content 层读到 chrome/android 层3.1 一条清晰的分层线AndroidChromium 不是“界面层 WebView 外壳”的传统浏览器 App而是把浏览器内核和 UI 放在同一个进程模型里分层管理。从源码目录角度看最典型的一条链是base - net - content - chrome。content层负责多进程、页面渲染、资源加载这些核心能力chrome层负责地址栏、书签、设置这些浏览器产品功能。你改变页面加载逻辑动content改标签页交互动chrome。这条分界线在标识符上也很直观content暴露给上层的是WebContents、BrowserContext、NavigationHandle这些偏底层的概念chrome层面出现的是ChromeTabbedActivity、TabModel、Omnibox这类偏产品化的概念。阅读源码时如果看到一个类不知道属于哪层先看它的 import 里有没有org.chromium.chrome再决定改的时候要遵循哪一层的接口约束。3.2 Android 特有目录Java UI、Native 服务与 JNI 桥Android 平台的特殊性在于UI 必须是 Java渲染内核几乎全是 C。AndroidChromium 的处理方式是把 Java 层做薄Native 层做厚二者通过 JNI 搭桥。这里有一张基于常见版本整理的目录对照表能帮你在 grep 代码时不至于找错地方。目录内部角色常见文件或目标chrome/android/java/src/org/chromium/chrome/browser/浏览器 UI 主线程与各组件入口ChromeTabbedActivity.javachrome/browser/android/浏览器侧 Native 状态与 JNI 反向回调各*_jni_headers生成物content/public/android/content 内核暴露给 Java 的接口ContentMain.java、WebContents.javacontent/browser/android/浏览器进程中 content 层的原生实现Binder、进程通信相关源码components/下载、书签、自动填充、安全检测等业务模块各组件自己的android/android_webview/把内核封装成系统 WebView APIAwBrowserContext.javaJNI 桥在这套工程里不是零散存在的而是用NativeMethods和generate_jni工具自动生成既有声明又有实现的绑定代码。修改 Java 层新增一个 native 方法时通常只需要在 Java 文件里写带NativeMethods的接口构建系统会生成对应的 C 头文件。这与直接手写JNI_OnLoad和在jni.h里登记函数完全不同习惯普通 Android JNI 方式的人一开始会找不到“注册函数”这是正常现象。3.3 从入口 Activity 反推一条调用链无论是排查启动问题还是改启动流程都要先找到入口。Chromium 的 Android 入口类历次重构后位置略有变化最快的方式不是记路径而是在源码里用一次 grep 定位。grep -rn class ChromeTabbedActivity --include*.java chrome/ | head -5 find chrome/ -name *ChromeTabbedActivity* -type f 2/dev/null第一条命令用文件名和类名双重匹配能在几秒内定位到当前版本的真实路径第二条命令把可能存在的构造相关文件也带出来。定位到文件之后从onCreate往后的调用链大致是ChromeTabbedActivity创建TabModelSelectorTabModelSelector维护多个Tab每个Tab里持有WebContents而WebContents才是真正对应到渲染进程的容器。看到这条链你就会明白想在 AndroidChromium 里“打开一个网页”并不是简单地 setContentView 加 WebView而是要在 Java 层准备好 tab再把 URL 通过WebContents下发到渲染进程。3.4 用 gn ls 建立组件地图阅读大型源码时最怕的是不知道哪些东西“可以编译”。Chromium 里所有可构建产物都叫 target分布在各个目录的BUILD.gn文件中。用gn ls可以列出指定目录下的 target这是比看文档实时得多的摸排方式。gn ls out/Release | grep -E chrome_public|chrome_apk | head -20out/Release是上一章生成过的构建目录gn ls会读取该目录的构建配置列出实际上能被 Ninja 识别的目标。grep -E chrome_public|chrome_apk把浏览器 APK 相关目标筛出来如果你想了解自己解压的这个压缩包有没有附带 WebView可以把过滤词换成system_webview。这样做的价值在于源码树里的同名目录可能有好几个 target只有经过 GN 解析、被构建系统接受的 target 才是你在改完代码后真正会编译的那个。4. 用 GN 配置、编译 AndroidChromium APK 的关键参数4.1 为什么是 GN 而不是 Gradle普通 Android 工程用 Gradle 管理依赖和构建变体AndroidChromium 用的是 GN 加 Ninja。GN 负责在BUILD.gn文件里做依赖分析与参数解析产出build.ninjaNinja 再根据这份文件做增量编译。两者定位不同GN 更接近 CMakeNinja 更接近 Make但它们都不负责依赖下载依赖是gclient管的事。所以常见的错误认知“改完 JAVA_HOME 再 sync 一下就能编译”在这里是行不通的GN 参数、gclient 依赖、系统工具链三者分别生效。4.2 常见 GN 参数表编译 AndroidChromium 前必须先建一个args.gn文件。它位于构建目录下例如out/Release/args.gn里面是以键值对形式写的构建参数。下面这份是我在一台 8 核 16 GB 的 Linux 机器上验证过的最小配置适合出 Debug 包和做源码调试。参数推荐值作用target_osandroid声明构建平台漏掉会走 Linux 桌面逻辑target_cpuarm64真机用 arm64模拟器可用 x64is_debugtrue开启调试符号和关闭优化方便断点is_component_buildfalseAndroid 下必须保持 false组件动态库不支持symbol_level1符号级别0 最小、1 可回溯、2 最全android_channelstable渠道名影响默认包名与渠道标识fieldtrial_testing_liketrue关闭随机抽样行为更容易复现target_cpu很值得单独说。如果你打算在模拟器上调试用x64会比 arm64 快很多但 APK 不能安装到真机上反过来arm64 的包在模拟器上会走转译启动慢且有些拖沓。symbol_level是一个性价比比较高的参数2的调试体验最完整但会让链接时间和包体明显上涨日常验证用1遇到 Native 崩溃再临时改成2重新编译。4.3 生成构建目录并开始编译参数写好后执行gn gen生成构建描述然后直接autoninja编译目标。这里建议用多行 args 文件而不是一长串--args因为参数一旦变多命令行转义很容易出错。mkdir -p out/Release cat out/Release/args.gn EOF target_os android target_cpu arm64 is_debug true is_component_build false symbol_level 1 android_channel stable fieldtrial_testing_like true EOF gn gen out/Release autoninja -C out/Release chrome_public_apkgn gen out/Release做的事是解读当前目录所有BUILD.gn结合out/Release/args.gn生成build.ninja。autoninja是 Ninja 的封装会自动根据机器核数计算并发比直接敲ninja更不容易压垮内存。chrome_public_apk是浏览器 APK 的构建目标产物会出现在out/Release/apks/ChromePublic.apk。首次编译通常要跑一两个小时这不是死机Ninja 会持续输出编译进度如果中间某次失败修完参数后重新执行autoninja只会增量编译失败点之后的 target不需要从头再来。4.4 构建期最常见的三类失败第一类是args.gn参数拼写或取值不合法典型报错是ERROR at //build/config/android/config.gni。这类问题通常出在抄了网上旧版本的参数组合比如is_component_buildtrue在 Android 上已经不被支持删掉那行重新gn gen即可。第二类是依赖缺失报错表现为编译到某个模块时提示找不到头文件或资源文件。常见原因是gclient sync --nohooks跳过了 hooks导致src/third_party/下部分工具链没拉全。解决方式是回到仓库根目录执行gclient runhooks不一定要重新 sync 全部仓库。第三类是构建过程内存不足表现为 Ninja 在链接阶段突然中断日志尾部能看到internal compiler error: Killed。常规缓解手段是把并发调低例如autoninja -C out/Release -j2 chrome_public_apk-j2表示只开两个并发任务。8 核机器默认并发 8但一个 C 链接动作就能吃满 4 GB 内存并发一高很容易被系统 OOM。代价是编译时间变长不过总比重启机器重跑来得可控。5. 拿到 AndroidChromium 构建产物后验证、联调与最小定制5.1 安装与确认 APK 可用编译成功不等于 APK 能安装进所有机器。Chromium 的 Debug APK 默认使用org.chromium.chrome作为包名这个包名在部分厂商 ROM 上可能被预装组件占用需要先卸载旧版本再安装。adb install -r out/Release/apks/ChromePublic.apk adb shell monkey -p org.chromium.chrome -c android.intent.category.LAUNCHER 1 adb logcat | grep cr_ | head -50adb install -r的-r表示覆盖安装如果提示签名冲突先adb uninstall org.chromium.chrome再装。monkey是一个不起眼但很有用的启动命令-p org.chromium.chrome限定包名-c android.intent.category.LAUNCHER指定启动入口最后一个数字1表示只触达一个 Activity不会像压力测试那样产生大量随机事件。启动后大概率能在 MainActivity 的调用链上看到标签cr_这是 Chromium 在 Android 上统一的日志前缀用它过滤比直接看全量 logcat 有效得多。5.2 一条改完就能重新编译的最小定制路径如果你只是想做一次“改源码再编译成功”的验证不要一上来就改界面布局。最便宜的改法是替换默认搜索引擎或主页字符串。Chromium 的搜索引擎预置数据集中在components/search_engines/下先用 grep 找到预置 URLgrep -rn google.com components/search_engines/ --include*.cc --include*.json | grep -v test | head把命中文件里的google.com替换成目标站点重新编译chrome_public_apk安装后触发一次搜索就能看到效果。这条路径改动范围小、编译时间短、出错了也好恢复。对比之下直接改ChromeTabbedActivity的布局风险很高因为它的 view 构建依赖大量模板资源和自适应逻辑改一行 XML 可能引发整条启动链不稳定。如果你的最终目标不是“改造一个浏览器”而是“让自己的 App 拥有 Chromium 内核”可以少走几步直接构建system_webview_apk并用它替换系统 WebView或者在此基础上封装自定义 WebView 接口。AndroidChromium 这套源码的价值并不是给你一个开箱可用的浏览器而是让你在需要内核级能力时不用从地址栏反推到底层。编译出一个能安装、能 grep、能改参数的 APK后续再深入就只是时间问题了。本文还有配套的精品资源点击获取