第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),插件和宿主共同依赖该库,从而在源码层面隔离。

宿主与插件模块解耦结构(RePlugin / Atlas 统一视角) 宿主 App (独立工程) Common Interface Lib (纯接口 + 数据类) RePlugin 插件 Module (plugin 类型) Atlas Bundle Module (bundle 类型) 解耦关键: 宿主和插件模块只能依赖 Common Interface Lib,不能相互依赖。 运行时通过框架 API 或反射获取插件实例,避免编译期直接引用。
工程建议:将 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 配置验证与常见错误排查

框架编译错误可能原因解决方案
RePluginPlugin xxx not found插件 APK 未正确放置或 applicationId 不匹配检查 applicationId 与宿主加载时的 ComponentName 一致
RePluginDuplicate class插件模块依赖了宿主已包含的类库使用 compileOnly 替代 implementation
AtlasBundle xxx not installedbundle 未在 bundleList 中声明,或动态部署失败在宿主 atlas.bundleConfig.bundleList 中加入 bundle 名称
AtlasClassNotFoundException接口实现类路径写错,或 bundle 未成功加载确认 bundle 的 bundleId 和类全限定名正确
调试技巧:在宿主 onCreate 中添加 Log.d("PluginTest", "Plugin installed: " + RePlugin.isPluginInstalled("myplugin")) 或 Atlas.getInstance().isBundleInstalled("shop_bundle") 来快速验证框架层是否正确识别插件模块。