Android Gradle 插件开发入门指南(二)

4,233 阅读2分钟

image.png
Android Gradle 插件开发入门指南一共规划了三篇文档:

目标

指南一中我们实现了一个简单的Gradle通用插件,可以应用到任何以Gradle为构建系统的项目里。指南二里,我们要让Gradle和Android真正在一起,实现一个修改Apk输出名称的Gradle插件,使Apk的文件名中包含应用名称、VersionCode、VersionName、生成类型(BuildType: Debug/Release)、打包日期、Flavor信息。
先给我们的app module添加qqbaidu两个Flavor信息(flavor名字随意,baidu、qq只是举例哈,别一根筋),方便待会验证插件功能,两种方式:

  1. 如果你对Android Gradle插件比较熟悉,直接在app的build.gradle脚本里写入变体信息

image.png

  1. 也可以借助Android Studio提供的Project Structure窗口设置

image.png
image.png
参考文档:developer.android.com/studio/buil…

实现

Gradle构建系统里有两个很重要的概念,一个就是我们反复提到的Task(任务),构建的基本单元;另一个就是我们即将用到的Extension(扩展),通过扩展属性可以定制构建过程和构建产物。
上面提到的诸多信息中,打包日期、应用名称属于通用信息我们可以直接得到,其他信息属于Android Gradle插件的扩展信息,我们要得到这些扩展信息,就得依赖(implementation)于Android Gradle插件,具体Android Gradle Plugin有哪些扩展信息,可以参考Android 插件 DSL 参考文档

配置插件Module

按照指南一的步骤,新建一个名为apkrename-plugin的插件Module,并添加对Android Gradle插件的依赖

dependencies {
    implementation 'com.android.tools.build:gradle:4.1.0'
}

依赖添加完成后,我们就可以在插件的apply函数里实现我们的功能了

实现插件功能

我们定义Apk的文件名称格式为:应用名称-VersionCodeVersionCode-VersionName-BuildTypeBuildType-Flavor-$Date.apk,下面我们一步一步获取我们需要的所有信息:

1. 获取应用名称

public void apply(Project project) {
    // 应用名称
    def projectName = project.name
}

2. 获取日期

public void apply(Project project) {
    // 应用名称
    def projectName = project.name
    // 打包日期,格式为:年月日时分秒
    def buildTime = new Date().format("yyyyMMddHHmmss")
}

3. 获取Android Gradle Plugin的扩展信息

apply函数的入参project对象包含了项目构建的所有扩展信息,我们这里只需要Android Gradle Plugin的扩展信息,这些信息是指什么呢?看文档啊:Extension types
image.png

public void apply(Project project) {
    // 应用名称
    def projectName = project.name
    // 打包日期,格式为:年月日时分秒
    def buildTime = new Date().format("yyyyMMddHHmmss")
    // project有很多扩展信息,我们这里需要Android相关的扩展信息,com.android.build.gradle.AppExtension
    def appExtension = project.extensions.getByType(AppExtension)
}

appExtension包含了Android Gradle Plugin的所有扩展信息,其中applicationVariants属性是所有构建变体信息的集合,我们遍历这个集合就能得到每个变体实例,进而得到变体的其他信息。
image.png
完整的信息获取代码:

public void apply(Project project) {
    // 应用名称
    def projectName = project.name
    // 打包日期,格式为:年月日时分秒
    def buildTime = new Date().format("yyyyMMddHHmmss")
    // project有很多扩展信息,我们这里需要Android相关的扩展信息,com.android.build.gradle.AppExtension
    def appExtension = project.extensions.getByType(AppExtension)
    // 遍历扩展信息里的所有变体,根据每个变体的信息组合出Apk的文件名称
    appExtension.applicationVariants.all { variant ->
        // 通过方法定义我们知道variant的实际类型为ApplicationVariant
        // val applicationVariants: DomainObjectSet<ApplicationVariant> =
        //         dslServices.domainObjectSet(ApplicationVariant::class.java)
        def versionCode = ((ApplicationVariant) variant).versionCode
        def versionName = ((ApplicationVariant) variant).versionName
        def buildType = ((ApplicationVariant) variant).buildType.name
        def flavor = ((ApplicationVariant) variant).flavorName
    }
}

如果你去看Android 插件 DSL 参考文档,并不能发现applicationVariants的描述文档,所以到这里文档就指望不上了,还好我机智,command+B定位到了方法声明的地方,发现applicationVariants元素类型是ApplicationVariant,而且ApplicationVariant只是个接口,具体的实现是ApplicationVariantImpl,有了这些信息,其他信息的获取水到渠成,顺便还发现了后面我们要用的属性outputs

4. 修改apk输出名称

apk文件名称属于任务的输出属性,那一定就在前面的outputs里,同样command+B发现outputs的元素类型为BaseVariantOutputBaseVariantOutput只是个接口,具体实现为ApkVariantOutputImpl,在ApkVariantOutputImpl类里并没找到修改apk输出名称的函数,但是ApkVariantOutputImpl同时还实现了ApkVariantOutput,这里面就有我们需要的setOutputFileName方法:

public void apply(Project project) {
    // 应用名称
    def projectName = project.name
    // 打包日期,格式为:年月日时分秒
    def buildTime = new Date().format("yyyyMMddHHmmss")
    // project有很多扩展信息,我们这里需要Android相关的扩展信息,com.android.build.gradle.AppExtension
    def appExtension = project.extensions.getByType(AppExtension)
    // 遍历扩展信息里的所有变体,根据每个变体的信息组合出Apk的文件名称
    appExtension.applicationVariants.all { variant ->
        // 通过方法定义我们知道variant的实际类型为ApplicationVariant
        // val applicationVariants: DomainObjectSet<ApplicationVariant> =
        //         dslServices.domainObjectSet(ApplicationVariant::class.java)
        def versionCode = ((ApplicationVariant) variant).versionCode
        def versionName = ((ApplicationVariant) variant).versionName
        def buildType = ((ApplicationVariant) variant).buildType.name
        def flavor = ((ApplicationVariant) variant).flavorName
        ((ApplicationVariant) variant).outputs.all { output ->
            // 变体输出的除了apk还有其他文件,这里我们只修改apk的文件名
            def outputFile = ((BaseVariantOutput) output).outputFile
            if (outputFile != null && outputFile.name.endsWith(".apk")) {
                ((ApkVariantOutput) output).outputFileName = "$projectName-$versionCode-$versionName-$buildType-$flavor-${buildTime}.apk"
            }
        }
    }
}

验证

插件功能开发完成了,发布插件到repo目录(不会的,回去看指南一),然后对app应用apkRename插件:

  • build.gradle
buildscript {
    repositories {
        ...
        // 如果是发布到MavenLocal就是用mavenLocal()
        // mavenLocal()
        // 自定义的发布地址
        maven { url('./repo') }
    }
    dependencies {
        ...
        classpath "com.lenebf.plugin:apk-rename:0.0.1"
    }
}
  • app/build.gradle
plugins {
    ...
    id 'com.lenebf.plugin.apk-rename'
}

执行assembleDebug任务看看输出的apk名称是否如我们所愿
image.png
一如既往的完美,真是佩服我自己
image.png

代码

文章中代码地址:github.com/lenebf/Grad…