插件开发实战:四大核心场景

说实话,插件开发这个话题,很多同学觉得门槛高。其实不然。

我刚开始接触 Gradle 插件时,也觉得这东西很神秘。后来做多了才发现,插件说白了就是一段能在构建过程中执行的代码。你写个 Task,注册到某个生命周期里,它就活了。

今天咱们聊四个实战场景:组件化插件、自动注册插件、资源合并插件、代码生成插件。这四个场景覆盖了日常开发中 80% 的插件需求。

核心观点:插件不是炫技,是解决重复劳动的工具。你想想看,每次手动注册 Activity、手动合并资源、手动写模板代码,这些事交给插件做,它不香吗?

Android Gradle 插件实战四大场景 Gradle 插件开发 组件化插件 自动注册插件 资源合并插件 代码生成插件 组件化插件 • 模块间解耦 • 路由自动配置 • 依赖版本统一 自动注册插件 • Activity/Service • ContentProvider • BroadcastReceiver 资源合并插件 • 多模块资源 • 资源去重 • 命名冲突解决 代码生成插件 • 模板代码生成 • 注解处理 • 编译期织入

一、组件化插件:模块化架构的基石

组件化,这个词大家都不陌生。但真正落地时,你会发现每个模块都要写一堆重复的配置:build.gradle 里的依赖、版本号、插件声明……

我个人习惯写一个 ComponentPlugin,把这些重复劳动统一管理。

1.1 核心思路

组件化插件要解决三个问题:

  • 依赖统一:所有模块的依赖版本由插件统一管理
  • 路由配置:自动注入 ARouter 或 WMRouter 的配置
  • 构建变体:区分 debug/release 的组件独立运行模式

我的经验:组件化插件不要写得太重。我见过有人把整个构建逻辑塞进插件里,结果出了问题很难排查。插件只做「配置注入」,具体逻辑交给各个模块的 build.gradle 去处理,这样更灵活。

1.2 代码示例

class ComponentPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        // 1. 统一依赖版本
        project.extensions.create("componentConfig", ComponentExtension::class.java)
        
        // 2. 根据模块类型注入不同配置
        project.afterEvaluate {
            val isApp = project.plugins.hasPlugin("com.android.application")
            if (isApp) {
                configureAppModule(project)
            } else {
                configureLibModule(project)
            }
        }
    }
    
    private fun configureAppModule(project: Project) {
        project.android {
            // 注入路由配置
            defaultConfig {
                javaCompileOptions {
                    annotationProcessorOptions {
                        arguments(mapOf(
                            "AROUTER_MODULE_NAME" to project.name,
                            "AROUTER_GENERATE_DOC" to "enable"
                        ))
                    }
                }
            }
        }
    }
}

二、自动注册插件:告别 AndroidManifest 手动配置

嗯,这里要注意。自动注册插件不是让你在运行时动态注册,而是在编译期把 Activity、Service 等信息写入 Manifest 文件。

我曾经在一个大型项目中,有 30 多个模块,每个模块都要手动在 Manifest 里声明 Activity。后来写了个自动注册插件,每次新增 Activity 只需要加个注解,插件自动处理 Manifest 合并。

2.1 实现原理

说白了就是三步:

  1. 扫描所有类文件,找到带 @AutoRegister 注解的类
  2. 解析注解参数(如 exportedintentFilter
  3. 生成 Manifest 片段,注入到合并流程中

避坑指南:我曾经在扫描类文件时用了全量扫描,导致构建时间增加了 30 秒。后来改用 ASM 在字节码层面处理,速度提升了很多。记住,插件开发一定要考虑性能。

2.2 关键代码

class AutoRegisterTransform : Transform() {
    override fun transform(transformInvocation: TransformInvocation) {
        val outputProvider = transformInvocation.outputProvider
        transformInvocation.inputs.forEach { input ->
            input.jarInputs.forEach { jarInput ->
                // 扫描 jar 包中的 class 文件
                val jarFile = jarInput.file
                val matchedClasses = scanJarForAnnotations(jarFile)
                
                // 生成 Manifest 片段
                val manifestFragment = generateManifestFragment(matchedClasses)
                writeManifestFragment(manifestFragment, outputProvider)
            }
        }
    }
}

三、资源合并插件:解决多模块资源冲突

资源合并,这个坑我踩过很多次。多个模块定义了同名的 ic_launcher.pngcolors.xml,合并时要么覆盖,要么报错。

资源合并插件的核心是:在合并之前做预处理

3.1 合并策略

冲突类型 默认行为 插件处理
同名资源 后加载的覆盖前加载的 自动添加模块前缀
相同内容 保留一份 去重并记录日志
命名不规范 不处理 自动重命名

我的建议:资源合并插件最好配合 lint 检查一起使用。在 CI 流程中,如果发现资源冲突,插件可以自动修复并提交 PR。这样能减少很多人工 review 的工作量。

四、代码生成插件:消灭模板代码

你想想看,每个新页面都要写 Activity、ViewModel、Layout、Adapter……这些模板代码写多了真的烦。

代码生成插件就是干这个的。它可以在编译期根据你的配置,自动生成这些模板文件。

4.1 生成策略

  • 基于模板:预定义 FreeMarker 或 KotlinPoet 模板
  • 基于注解:通过注解处理器生成代码
  • 基于配置:读取 YAML 或 JSON 配置文件生成

4.2 实战示例

class CodeGeneratorPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.task("generatePage") {
            doLast {
                val template = project.rootProject.file("templates/PageTemplate.kt")
                val outputDir = project.projectDir.resolve("src/main/java/com/example")
                
                // 读取模板并替换占位符
                val content = template.readText()
                    .replace("\${package}", "com.example")
                    .replace("\${className}", "MainActivity")
                
                // 写入生成的文件
                outputDir.resolve("MainActivity.kt").writeText(content)
            }
        }
    }
}

核心要点:代码生成插件一定要支持「增量生成」。我见过一个插件每次运行都全量生成,结果把开发者手动修改的代码覆盖了。后来我们加了文件比对逻辑,只生成不存在的文件,或者文件内容有差异时才覆盖。

总结

这四个插件场景,覆盖了 Android 构建过程中的大部分痛点。组件化插件解决架构问题,自动注册插件解决配置问题,资源合并插件解决冲突问题,代码生成插件解决效率问题。

我个人觉得,插件开发最难的不是技术,而是「想清楚要解决什么问题」。你写插件之前,先问问自己:这个流程能不能自动化?这个配置能不能统一管理?这个模板能不能自动生成?想清楚了,写起来就顺了。


公众号:蓝海资料掘金营,微信deep3321