第3章 插件模块创建与 Gradle 配置
本章聚焦于如何在 Android 工程中分别创建 RePlugin 插件模块与 Atlas bundle 模块,并完成 Gradle 配置以达到宿主与插件模块的真正解耦。你将掌握两个框架在构建层面的核心差异、依赖声明方式以及解耦设计原则,最终能够独立搭建一个可运行的多框架插件化应用骨架。
学习目标:完成本章后,学员应能:
- 新建 RePlugin 插件 module 并配置
build.gradle,使插件可被宿主识别和加载 - 在宿主项目中声明 Atlas bundle 依赖,并使 bundle 参与构建
- 通过接口完全解耦插件模块与宿主模块,消除编译期直接依赖
- 识别两个框架在 Gradle 配置上的关键差异并能快速排错
3.1 插件模块的结构与解耦原则
无论是 RePlugin 还是 Atlas,插件模块在物理上都是一个独立的 Android Library 工程。宿主模块通过框架的构建插件将插件模块的产物(APK / AAR)集成进来,但编译期不能出现直接引用(即避免 import 具体类)。解耦的常见方案是定义公共接口库(common-lib),插件和宿主共同依赖该库,从而在源码层面隔离。
工程建议:将 Common Interface Lib 放在宿主工程根目录下的
common 子模块中,所有插件和宿主都通过 api project(':common') 依赖它。这样可以避免因接口变更导致的多处修改。
3.2 RePlugin 插件 Module 的 build.gradle 配置
RePlugin 插件模块的构建依赖专门的 Gradle 插件 replugin-plugin-gradle。以下是一个完整的 build.gradle 配置示例:
// plugin_module/build.gradle
apply plugin: 'com.android.library'
// 应用 RePlugin 插件 Gradle 插件
apply plugin: 'replugin-plugin-gradle'
android {
compileSdkVersion 33
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
// 插件需要指定 applicationId,用于在宿主中唯一标识
applicationId "com.example.myplugin"
versionCode 1
versionName "1.0.0"
}
buildTypes {
release {
minifyEnabled false
}
}
}
dependencies {
// 公共接口库
api project(':common')
// 如果插件需要依赖 RePlugin 框架 API(可选)
compileOnly 'com.qihoo360.replugin:replugin-api-lib:2.3.4'
}
// RePlugin 插件 Gradle 扩展配置
repluginPluginConfig {
// 插件名称,宿主通过此名称加载
pluginName = "myplugin"
// 插件版本号
pluginVersion = 1
// 是否开启 BackgroundDex(用于解决 64k 问题)
backgroundDexEnable = false
}
| 配置项 | 说明 | 典型值 |
|---|---|---|
applicationId | 插件唯一标识,宿主根据此 ID 加载插件 | com.example.xxx |
repluginPluginConfig.pluginName | 宿主代码中使用的引用名称 | "myplugin" |
repluginPluginConfig.pluginVersion | 内部版本,宿主可做版本校验 | 1 |
compileOnly | 只用于编译,不参与打包(避免框架冲突) | replugin-api-lib |
常见错误:如果在插件模块中使用
implementation 依赖了宿主中的类,宿主编译时会报 Duplicate class 错误。务必只依赖 :common 模块或框架 API,宿主类仅在运行时通过框架 SDK 获取。
3.3 Atlas Bundle 依赖声明与模块配置
Atlas 使用 bundle 作为插件单元。首先需要在宿主工程的 build.gradle 中启用 Atlas Gradle 插件并声明 bundle 列表。然后在 bundle 模块中配置 bundle 特性。
3.3.1 宿主工程声明 bundle(settings.gradle 和宿主 build.gradle)
// settings.gradle 中引入 bundle 模块
include ':bundle_shop'
project(':bundle_shop').projectDir = new File('bundles/shop')
// 宿主 build.gradle
apply plugin: 'com.android.application'
apply plugin: 'com.taobao.atlas.application'
atlas {
bundleConfig {
// 声明需要打包的 bundle
bundleList = ['bundle_shop']
// 动态部署模式(可选)
dynamicEnable = true
}
}
dependencies {
// 宿主依赖公共接口库
api project(':common')
// 注意:不要直接 implementation bundle 模块!
// Atlas 会通过 bundleList 自动处理打包和解耦
}
3.3.2 Bundle 模块的 build.gradle 配置
// bundle_shop/build.gradle
apply plugin: 'com.android.library'
// 应用 Atlas bundle 插件
apply plugin: 'com.taobao.atlas.bundle'
android {
compileSdkVersion 33
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
}
}
dependencies {
// 公共接口库
api project(':common')
// bundle 可以依赖其他 bundle 或宿主中的基础模块
// 但要避免编译期引用宿主 Application 等具体类
}
// Atlas bundle 扩展配置
atlas {
bundleName = 'shop_bundle'
// 指定 bundle 的独立 applicationId
bundleId = 'com.example.shop'
// 是否自动注册(Atlas3.0+ 支持)
autoRegister = false
}
关键区别:RePlugin 的插件模块是一个独立的 Library,通过
replugin-plugin-gradle 插件直接将插件打包为 APK;而 Atlas 的 bundle 本质也是 Library,但依赖宿主工程的 bundleList 进行聚合打包。两者都要求不能直接 implementation 宿主模块。
3.4 宿主 Module 与插件 Module 的解耦实现
解耦的核心是:宿主只通过 接口 + 框架加载 API 获取插件实例,而不引用插件中的任何具体类。两种框架在此思路下有不同的实现方式。
3.4.1 RePlugin 宿主侧加载代码示例
// 宿主中加载插件的典型代码
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. 加载插件(插件 APK 需要预先放置在 assets 目录或通过下载获得)
String pluginPath = getFilesDir() + "/myplugin.apk";
RePlugin.loadPlugin(pluginPath);
// 2. 通过接口获取插件中的服务
IShopService shopService = (IShopService) RePlugin.startActivity(
this,
new Intent().setComponent(
new ComponentName("com.example.myplugin",
"com.example.myplugin.ShopActivity"))
);
// 或者通过插件提供者模式获取接口实例
}
}
3.4.2 Atlas 宿主侧加载代码示例
// 宿主中通过 Atlas 框架加载 bundle
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. 确保 bundle 已安装(Atlas 通常预置在 assets 中)
Atlas.getInstance().installBundle("shop_bundle");
// 2. 通过反射或接口获取 bundle 内的类(推荐使用公共接口)
// 假设接口 IShopService 在 common 模块中定义
IShopService service = (IShopService) Class
.forName("com.example.shop.ShopServiceImpl")
.newInstance();
service.showShopPage(this);
}
}
3.4.3 接口定义示例(Common Interface Lib)
// common/src/main/java/com/example/common/IShopService.java
public interface IShopService {
void showShopPage(Activity activity);
}
// 插件模块中实现:
// com.example.myplugin.ShopServiceImpl implements IShopService { ... }
// Atlas bundle 中实现:
// com.example.shop.ShopServiceImpl implements IShopService { ... }
解耦检查清单:
- ✅ 宿主
build.gradle中 不包含 任何插件模块的implementation project。 - ✅ 插件模块中 不包含 宿主具体类的
import(如宿主 Application 类)。 - ✅ 所有跨模块调用均通过 common 接口 + 框架提供的加载 API 实现。
- ✅ 接口变更时只需修改 common 模块,并重新编译所有模块。
3.5 配置验证与常见错误排查
| 框架 | 编译错误 | 可能原因 | 解决方案 |
|---|---|---|---|
| RePlugin | Plugin xxx not found | 插件 APK 未正确放置或 applicationId 不匹配 | 检查 applicationId 与宿主加载时的 ComponentName 一致 |
| RePlugin | Duplicate class | 插件模块依赖了宿主已包含的类库 | 使用 compileOnly 替代 implementation |
| Atlas | Bundle xxx not installed | bundle 未在 bundleList 中声明,或动态部署失败 | 在宿主 atlas.bundleConfig.bundleList 中加入 bundle 名称 |
| Atlas | ClassNotFoundException | 接口实现类路径写错,或 bundle 未成功加载 | 确认 bundle 的 bundleId 和类全限定名正确 |
调试技巧:在宿主
onCreate 中添加 Log.d("PluginTest", "Plugin installed: " + RePlugin.isPluginInstalled("myplugin")) 或 Atlas.getInstance().isBundleInstalled("shop_bundle") 来快速验证框架层是否正确识别插件模块。