1.Android 3分钟跑通腾讯 Shadow 插件化官方Demo:零反射、手把手实战(基于源码依赖)

Tencent Shadow 是业界知名的"零反射"、"零 Hack"的 Android 插件化框架,以其高稳定性与灵活性著称。

本文将以 源码依赖方式(projects/sample/source 为蓝本,手把手带你成功运行官方 Demo。


1. 源码地址与文档

两种 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应用由三大部分组成:

  1. 宿主(Host) :空壳App,提供运行环境 - sample-host

  2. 管理器(Manager) :负责插件的下载、安装和版本管理 - sample-manager

  3. 插件(Plugin) :业务核心,包含:

    • Loader :定义组件配对关系 - sample-loader
    • Runtime :提供壳子代理组件的实际实现 - sample-runtime
    • 业务代码 :真正的业务逻辑 - sample-appsample-base

表格

组件 作用 对应模块
宿主(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.apk
  • build/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.apk
  • sample-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 运行效果

  1. 启动 sample-host App

  2. 点击 "Load Plugin" 按钮

  3. 成功跳转至插件页面,显示:

    csharp 复制代码
    Hello 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 插件化的灵魂任务,主要完成:

  1. 编译 loaderruntimeapp 模块为 APK
  2. 对 APK 进行插件化改造(如重排资源 ID)
  3. 打包为标准插件 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 未生成。

解决方案

  1. 确保在 Shadow/ 根目录 执行命令
  2. 手动运行 ./gradlew packageDebugPlugin
  3. 检查 build/outputs/ 下是否存在对应文件
  4. 不要直接点击 AS 的 Run 按钮 (会 clean 产物),改用 adb install

7. 总结与心得

  1. 架构清晰是核心
    理解"宿主-管理器-插件(Loader/Runtime/业务)"分层,是后续定制与排错的基础。
  2. 构建流程是关键
    Shadow 通过自定义 Gradle 插件(packagePlugin)完成 APK 插件化改造,理解此过程对 CI/CD 集成至关重要。
  3. 源码方式最利于学习
    projects/sample/source 将 SDK 与 Demo 放在同一工程,便于跟踪从源码到 APK 的完整链路。
  4. 问题定位有方法
    遇到"文件不存在",先检查 build/outputs/ 是否有产物,再看 Gradle 日志。
相关推荐
「、皓子~1 小时前
AI 创作系列(34)海狸IM桌面版 v1.1 正式发布:Vite + Electron 性能优化与体验升级
前端·人工智能·electron·开源·开源软件·im
似霰1 小时前
HIDL Hal 开发笔记8----添加硬件访问服务
android·framework·hal
光影少年1 小时前
electron通信方式有哪些?
前端·javascript·electron
CodeSheep1 小时前
这个老牌知名编程论坛,彻底倒下了!
前端·后端·程序员
SY_FC2 小时前
niapp开发的 H5 被app嵌套,H5调用ios和安卓方法
android·ios·cocoa
BD_Marathon2 小时前
搭建MyBatis框架之创建mapper接口(四)
java·前端
meichaoWen2 小时前
【nodejs】nodejs的一些基础知识
开发语言·前端·javascript
假装我不帅2 小时前
rider开发asp.net webform项目
android·okhttp·asp.net
@Autowire2 小时前
Grid-grid-template-areas 属性
前端·javascript·css