Tencent Shadow 是业界知名的"零反射"、"零 Hack"的 Android 插件化框架,以其高稳定性与灵活性著称。
本文将以 源码依赖方式(
projects/sample/source) 为蓝本,手把手带你成功运行官方 Demo。

1. 源码地址与文档
- GitHub 地址 :github.com/Tencent/Sha...
- Sample 文档 :projects/sample/README.md
两种 Sample 示例
| 类型 | 路径 | 适用场景 |
|---|---|---|
| 源码依赖 | projects/sample/source |
学习、研究、修改 Shadow 源码 |
| Maven 依赖 | projects/sample/maven |
快速集成到已有项目 |
✅ 本文聚焦于 源码依赖方式。
2. 项目结构概览

csharp
projects/sample/source/
├── sample-host/ # 宿主应用(空壳)
├── sample-manager/ # 插件管理器(负责插件加载、版本管理)
├── sample-plugin/ # 插件相关模块
│ ├── sample-loader/ # Loader:定义插件组件 ↔ 宿主壳子的映射关系
│ ├── sample-runtime/ # Runtime:壳子代理组件的实际实现
│ ├── sample-base-lib/ # 基础业务库(AAR)
│ ├── sample-base/ # 基础插件 APK(打包 base-lib)
│ └── sample-app/ # 主业务插件(依赖 base-lib)
├── sample-constant/ # 公共常量(如插件类名、进程名等)
└── build.gradle / settings.gradle
🔍 Shadow 应用整体架构
一个完整的 Shadow 应用由三大部分组成: Shadow应用由三大部分组成:
-
宿主(Host) :空壳App,提供运行环境 -
sample-host -
管理器(Manager) :负责插件的下载、安装和版本管理 -
sample-manager -
插件(Plugin) :业务核心,包含:
- Loader :定义组件配对关系 -
sample-loader - Runtime :提供壳子代理组件的实际实现 -
sample-runtime - 业务代码 :真正的业务逻辑 -
sample-app和sample-base
- Loader :定义组件配对关系 -
表格
| 组件 | 作用 | 对应模块 |
|---|---|---|
| 宿主(Host) | 提供运行环境、代理 Activity | sample-host |
| 管理器(Manager) | 管理插件下载、安装、生命周期 | sample-manager |
| 插件(Plugin) | 包含业务逻辑 | sample-plugin/... |
| ↳ Loader | 映射插件组件与宿主壳子 | sample-loader |
| ↳ Runtime | 实现壳子代理的真实逻辑 | sample-runtime |
| ↳ 业务 App | 真正的业务代码(如 MainActivity) | sample-app, sample-base |
💡 关键结论 :Shadow 不是一个 APK,而是一个 宿主 + 管理器 + 插件包(Loader + Runtime + 业务) 的组合体。
3. 构建与运行 Demo
3.1 环境准备
✅ 正确打开工程方式:
- 错误做法 :只打开
Shadow/projects/sample/source - 正确做法 :打开整个
Shadow/根目录
原因:
source示例通过includeBuild '../..'依赖上层 SDK 源码(位于sdk/目录)。若只打开子目录,Gradle 无法解析依赖,同步失败。
✅ 推荐环境配置:
表格
| 工具 | 版本 | 说明 |
|---|---|---|
| JDK | 11 或 17 | 不推荐 Java 21+ (与 Gradle 7.5 不兼容) |
| Gradle | 7.5 | 项目自带(gradle-7.5-bin.zip) |
| Android Gradle Plugin | 7.4.2 | 见 build-logic 配置 |
📌 验证命令:
./gradlew --version
3.2 构建流程核心步骤(关键四步)
⚠️ 若直接在 Android Studio 中运行
sample-host,可能因构建中断导致"文件不存在"错误。建议手动执行以下步骤。
步骤 1:打包插件 ZIP
bash
# 在 Shadow/ 根目录执行
/gradlew packageDebugPlugin
✅ 生成产物:
sample-app/build/outputs/apk/plugin/debug/sample-app-plugin-debug.apkbuild/plugin-debug.zip← 核心插件包sample-loader/build/outputs/apk/debug/sample-loader-debug.apk
会生成
lua
F:\shadow1021\Shadow-master\projects\sample\source\sample-plugin\
sample-app\build\outputs\apk\plugin\debug\sample-app-plugin-debug.apk
build/plugin-debug.zip
也会生成这个:
lua
F:\shadow1021\Shadow-master\projects\sample\source\sample-plugin\
sample-loader\build\outputs\apk\debug\sample-loader-debug.apk
步骤 2:构建宿主与管理器
bash
./gradlew assembleDebug
✅ 生成:
sample-host-debug.apksample-manager-debug.apk
会生成:
lua
F:\shadow1021\Shadow-master\projects\sample\source\sample-manager\build\outputs\apk\debug
\sample-manager-debug.apk
同时也会生成这个:
lua
F:\shadow1021\Shadow-master\projects\sample\source\sample-host\build\outputs\apk\debug\sample-host-debug.apk
步骤 3:推送插件到设备(模拟"下载")
bash
# 推送插件包
adb push build/plugin-debug.zip /data/local/tmp/
# 推送管理器 APK
adb push projects/sample/source/sample-manager/build/outputs/apk/debug/sample-manager-debug.apk /data/local/tmp/
💡 Shadow Demo 未实现真实下载 ,而是从
/data/local/tmp/读取文件。
步骤 4:安装并运行宿主
lua
adb install -r -t -d projects/sample/source/sample-host/build/outputs/apk/debug/sample-host-debug.apk
-t允许测试 APK,-d允许降级安装。
3.3 运行效果
-
启动
sample-hostApp -
点击 "Load Plugin" 按钮
-
成功跳转至插件页面,显示:
csharpHello Shadow Plugin! This is sample-app plugin.

3.4 关键代码位置
表格
| 功能 | 文件路径 |
|---|---|
| 宿主 Application | sample-host/src/main/java/com/tencent/shadow/sample/host/HostApplication.java |
| 插件加载逻辑 | sample-host/src/main/java/com/tencent/shadow/sample/host/PluginHelper.java |
| 插件业务页面 | sample-plugin/sample-app/src/main/java/com/tencent/shadow/sample/app/MainActivity.java |
| 公共常量 | sample-constant/src/main/java/com/tencent/shadow/sample/constant/Constant.java |
⚠️ 注意 :
Constant.java中定义的插件类名、进程名等,宿主与插件必须完全一致,否则无法找到 Activity。
4. 核心机制解析
4.1 packageDebugPlugin 命令做了什么?
这是 Shadow 插件化的灵魂任务,主要完成:
- 编译
loader、runtime、app模块为 APK - 对 APK 进行插件化改造(如重排资源 ID)
- 打包为标准插件 ZIP:
plugin-debug.zip
4.2 ./gradlew assembleDebug
这是标准的Android构建命令,会编译并打包指定模块的Debug版本APK。在Shadow项目中,你可以在各个模块目录下执行它来单独构建:
在 sample-manager 下执行,生成插件管理器APK。
在 sample-host 下执行,生成宿主APK。
4.3 plugin-debug.zip 内部结构
解压后包含:
python
plugin-debug.zip/
├── sample-loader-debug.apk # 插件加载器(Loader)
├── sample-runtime-debug.apk # 运行时环境(Runtime)
├── sample-app-plugin-debug.apk # 业务插件(经插件化处理)
├── config.json # 插件元数据与依赖声明
└── checksum.txt # 校验文件(可选)
表格
| 文件 | 类型 | 职责 | 类比 |
|---|---|---|---|
loader-debug.apk |
APK | 定义插件组件 ↔ 宿主壳子映射 | "翻译官" |
runtime-debug.apk |
APK | 实现壳子代理的真实逻辑 | "替身演员" |
app-plugin-debug.apk |
APK | 业务代码与 UI | "剧本" |
config.json |
JSON | 描述插件依赖与配置 | "说明书" |
4.4 plugin-debug.zip 是怎么自动被打包的?
通过 Gradle 插件 com.tencent.shadow 在宿主构建时自动触发插件模块的 packagePlugin 任务完成打包。
核心流程
1. 触发时机
当你在 Android Studio 运行 sample-host 模块时,Gradle 构建流程会自动执行:
less
// 在 sample-host/build.gradle 中有这样的依赖配置
shadow {
// 当构建宿主时,自动打包插件
packagePlugin {
pluginTypes = ['debug', 'release']
}
}
2. 执行任务
ruby
# 自动执行以下两个任务:
./gradlew :projects:sample:source:sample-plugin:sample-app:packageDebugPlugin
./gradlew :projects:sample:source:sample-plugin:sample-base:packageDebugPlugin
源码在哪?关键三处:
1. 打包任务定义
在 SDK 源码中:
👉 sdk/gradle/plugin/src/main/kotlin/com/tencent/shadow/gradle/PluginProject.kt
这里注册了核心任务:
javascript
tasks.register("package${variant.name.capitalize()}Plugin", Zip::class) {
// 把 loader、runtime、插件 APK、config.json 打包进 ZIP
}
这个任务就是 packageDebugPlugin 的来源。
2. ZIP 包内容从哪来?
看 sample-app/build.gradle(你的插件模块):
ini
shadow {
dependsOn = ['sample-base'] // 声明依赖
}
Shadow 插件会根据这个配置:
- 自动构建
sample-loader→ 得到loader.apk - 自动构建
sample-runtime→ 得到runtime.apk - 构建
sample-app并转成插件格式 →sample-app-plugin-debug.apk - 生成
config.json(描述依赖关系)
然后全部塞进 ZIP。
3. 宿主怎么拿到 ZIP?
在 sample-host 的构建脚本中(通常在 buildScripts/host-build.gradle 或类似):
csharp
// 构建宿主前,自动复制 ZIP 到 assets/
preBuild.doFirst {
copy {
from "../build/plugin-debug.zip"
into "src/main/assets/"
}
}
所以你运行 sample-host 时,插件包已经在 assets/plugin-debug.zip 里了。
5、核心调用链分析
makefile
sample-hello-api: 定义宿主API接口
↓
sample-hello-api-holder: 将API动态化
↓
sample-hello-apk: 生成APK
↓
sample-hello-host: 宿主应用
↓
sample-manager: 插件版本管理(核心)
↓
HostApplication: 宿主入口
↓
Shadow.getPluginManager(apk)
↓
PluginManager → DynamicPluginManager → ManagerImplLoader
↓
PluginHelper → ManagerFactoryImpl
5.2 关键代码位置
| 功能 | 文件路径 |
|---|---|
| 插件加载入口 | sample-host/src/main/java/.../HostApplication.java |
| 加载逻辑 | sample-host/src/main/java/.../PluginHelper.java |
| 插件业务页面 | sample-plugin/sample-app/src/main/java/.../MainActivity.java |
| 常量定义 | sample-constant/src/main/java/.../Constant.java |
注意 :Constant.java中定义了插件类名、进程名等,宿主和插件必须一致。
6. 常见问题与解决
❌ 错误:"plugin file not exist..."
原因 :构建中断,plugin-debug.zip 或 APK 未生成。
解决方案:
- 确保在 Shadow/ 根目录 执行命令
- 手动运行
./gradlew packageDebugPlugin - 检查
build/outputs/下是否存在对应文件 - 不要直接点击 AS 的 Run 按钮 (会 clean 产物),改用
adb install
7. 总结与心得
- 架构清晰是核心
理解"宿主-管理器-插件(Loader/Runtime/业务)"分层,是后续定制与排错的基础。 - 构建流程是关键
Shadow 通过自定义 Gradle 插件(packagePlugin)完成 APK 插件化改造,理解此过程对 CI/CD 集成至关重要。 - 源码方式最利于学习
projects/sample/source将 SDK 与 Demo 放在同一工程,便于跟踪从源码到 APK 的完整链路。 - 问题定位有方法
遇到"文件不存在",先检查build/outputs/是否有产物,再看 Gradle 日志。