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 日志。
相关推荐
_AaronWong29 分钟前
Electron 实现仿豆包划词取词功能:从 AI 生成到落地踩坑记
前端·javascript·vue.js
cxxcode29 分钟前
I/O 多路复用:从浏览器到 Linux 内核
前端
用户54330814419438 分钟前
AI 时代,前端逆向的门槛已经低到离谱 — 以 Upwork 为例
前端
JarvanMo41 分钟前
Flutter 版本的 material_ui 已经上架 pub.dev 啦!快来抢先体验吧。
前端
恋猫de小郭1 小时前
AI 可以让 WIFI 实现监控室内人体位置和姿态,无需摄像头?
前端·人工智能·ai编程
哀木1 小时前
给自己整一个 claude code,解锁编程新姿势
前端
程序员鱼皮1 小时前
GitHub 关注突破 2w,我总结了 10 个涨星涨粉技巧!
前端·后端·github
UrbanJazzerati1 小时前
Vue3 父子组件通信完全指南
前端·面试
是一碗螺丝粉1 小时前
5分钟上手LangChain.js:用DeepSeek给你的App加上AI能力
前端·人工智能·langchain
wuhen_n2 小时前
双端 Diff 算法详解
前端·javascript·vue.js