Android 基于Gradle 7.2+,自定义插件示例(实测7.4.2也可以运行)

Posted 匆忙拥挤repeat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 基于Gradle 7.2+,自定义插件示例(实测7.4.2也可以运行)相关的知识,希望对你有一定的参考价值。

文章目录

起源

前几天,找到了之前从官方公众号中,收藏的文章,来简单学习一波Gradle与AGP
就是这一篇:Gradle 与 AGP 构建 API: 进一步完善您的插件!,文章内部会发现,这是一个小系列,共有3篇文章。
文章对应的官方代码示例 gradle-recipes

示例中用的是 gradle 7.1的alpha版本,而我这边由于工作需要,本地升级过其它项目到7.2了。
结果呢,文章中的示例代码,大部分在我这运行不起来。真是怀疑人生啊!!!
后来,由于不能运行 ProcessBuilder,升级了gradle到 7.4.2,然而,发现还是不能在task中运行ProcessBuilder 运行命令。
靠不断的猜测,查资料,遂有了本篇。

截止到目前(2022-04-27),gradle已支持到7.4.2了。
对应的 gradle plugin 版本需要是 7.2+


环境配置

工程下的 build.gradle

// all buildscript  blocks must appear before any plugins  blocks in the script
buildscript  // gradle 构建脚本 自身,所需的
    repositories 
        google()
        mavenCentral()
    

    dependencies 
        // https://mvnrepository.com/artifact/com.android.tools.build/gradle?repo=google
        classpath("com.android.tools.build:gradle:7.2.0-rc01")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20")
    


// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins 
    id 'com.android.application' version '7.2.0-rc01' apply false
    id 'com.android.library' version '7.2.0-rc01' apply false
    id 'org.jetbrains.kotlin.android' version '1.6.20' apply false


//apply plugin: CustomPlugin

task clean(type: Delete) 
    delete rootProject.buildDir

为了编译插件,在工程下创建 buildSrc目录。
为了使用kotlin,将build.gradle 增加.kts后缀。

buildSrc/build.gradle.kts

plugins 
    `kotlin-dsl` // 使用这个后  buildSrc/src/main/kotlin 才成为源码目录


repositories  // project 所需的 repos
    google()
    mavenCentral()


dependencies  // project 所需的 depends
    // 这个必须依赖,不然一些 gradle 中的api 无法使用
    implementation("com.android.tools.build:gradle-api:7.1.3")
    implementation(kotlin("stdlib", version = "1.6.20"))
    // val ga = gradleApi() // 暂没看出来有什么作用
    // print: " gradle 2api: null unspecified null "
    // println("gradle api: $ga.group $ga.name $ga.version")


插件中增加变体variant(示例)

什么是变体,其实就是 android 中配置的 buildType 。
也是,如下图的东西

示例中,会增加一个 staging的变体。通过插件来完成。

代码不复杂,直接贴了

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.register
import  com.android.build.api.variant.ApplicationAndroidComponentsExtension

class CustomPlugin : Plugin<Project> 
	// 在哪里 apply plugin,那里所属的项目或模块就是这个 project 对象
    override fun apply(project: Project) 
    	addVariant(project, "staging")
    
	
	// 添加构建变体
    private fun addVariant(project: Project, variantName: String) 
        val extension = project.extensions.getByName(
            "androidComponents"
        ) as ApplicationAndroidComponentsExtension // android应用组件扩展

        // finalizeDsl, 在 DSL 对象应用于 Variant 创建前对它们进行修改
        // 在阶段结束时,AGP 将会锁定 DSL 对象,这样它们就无法再被更改
        extension.finalizeDsl  ext->
            println("finalizeDsl")
            ext.buildTypes.create(variantName).let  buildType ->
                buildType.initWith(ext.buildTypes.getByName("debug"))
                buildType.manifestPlaceholders["hostName"] = "example.com"
                buildType.applicationIdSuffix = ".debug$variantName"
                buildType.isDebuggable = true
                buildType.isShrinkResources = false // 是否压缩资源
                buildType.isMinifyEnabled = false // 是否混淆
            
        
		// 在每个变体锁定之前
        extension.beforeVariants  variantBuilder ->
            println("beforeVariants $variantBuilder.name")
            if (variantBuilder.name == variantName) 
                variantBuilder.enableUnitTest = false
                variantBuilder.minSdk = 23
            
        
    

在android application 模块的 build.gradle.kts 中应用 插件:
apply<CustomPlugin>()
然后在命令行执行命令
./gradlew :app:compileStagingSources
输出:

finalizeDsl
beforeVariants debug
beforeVariants release
beforeVariants staging

创建变体还有一些简单的方式,我的示例中,试了 “构建变体,方式1、2、3” ,就定义在 app/build.gradle.kts 中。


插件中修改manifest的version code值(示例)

这个示例,用到了 task,还是一个task套另一个task。还有文件输出与输入,manifest读取与合并等。
本来官方的示例,是以读取 git commint-id的 short 的 sha-1值 ,来改写 androidmanifest 中的 version code值。结果,并不能运行那段命令代码,搜索了一堆资料,也没搞定,放弃了。看了官方的示例,把那段也注释掉了,真有意思,哈哈。 所以也和他们一样,简单向文件内写入个数字。

GitVersionTask

package gitversion

import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import java.io.BufferedReader
import java.io.InputStreamReader

abstract class GitVersionTask : DefaultTask() 

    // RegularFileProperty, 表示某个可配置的常规文件位置,其值是可变的。
    @get:OutputFile
    abstract val gitVersionOutputFile: RegularFileProperty

    @TaskAction
    fun taskAction() 
		// 向文件内写入字符串
        gitVersionOutputFile.get().asFile.writeText("3234")
	

ManifestTransformerTask

package gitversion

import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction

abstract class ManifestTransformerTask: DefaultTask() 

   @get:InputFile
   abstract val gitInfoFile: RegularFileProperty

   @get:InputFile
   abstract val mergedManifest: RegularFileProperty

   @get:OutputFile
   abstract val updatedManifest: RegularFileProperty

   @TaskAction
   fun taskAction() 
      val gitVersion = gitInfoFile.get().asFile.readText()
      var manifest = mergedManifest.asFile.get().readText()
      manifest = manifest.replace(
         "android:versionCode=\\"1\\"",
         "android:versionCode=\\"$gitVersion\\""
      )

      // 先 get或先 asFile,都可以的,最终都是 File 对象
      // updatedManifest.get().asFile.writeText(manifest)
      updatedManifest.asFile.get().writeText(manifest)
   

@get:InputFile 用于读取数据
@get:OutputFile 用于写入数据

GitVersionPlugin

package gitversion

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.register
import java.io.File
import com.android.build.api.variant.AndroidComponentsExtension
import org.gradle.api.tasks.TaskProvider
import org.gradle.kotlin.dsl.getByType
import com.android.build.api.artifact.SingleArtifact

class GitVersionPlugin: Plugin<Project> 

    override fun apply(project: Project) 
        // ./gradlew :app:gitVersionProvider
        val gitVersionProvider = project.tasks.register<GitVersionTask>("gitVersionProvider") 
            // 对输出文件属性赋值一个文件对象 app/build/intermediates/gitVersionProvider/output
            gitVersionOutputFile.set(File(project.buildDir, "intermediates/gitVersionProvider/output"))
            outputs.upToDateWhen  false  // 将 upToDateWhen 设置为 false,这样此 Task 前一次执行的输出就不会被复用
        

        val androidComponents = project.extensions.getByType(
            AndroidComponentsExtension::class
        )
        androidComponents.onVariants  variant ->
            // staging/debug/release ManifestUpdater,   $ ./gradlew :app:stagingManifestUpdater
            // 输出文件位置:app/build/intermediates/merged_manifest/staging/[build variant]ManifestUpdater/AndroidManifest.xml
            val manifestUpdater: TaskProvider<ManifestTransformerTask> = project.tasks.register<ManifestTransformerTask>(variant.name + "ManifestUpdater") 
                // 设置输入(对 ManifestTransformerTask来说,是输入),输入的是 GitVersionTask 的输出 gitVersionOutputFile
                gitInfoFile.set(gitVersionProvider.get().gitVersionOutputFile)
                // gitInfoFile.set(gitVersionProvider.flatMap 
                //     it.gitVersionOutputFile
                // )
            
            variant.artifacts.use(manifestUpdater)
                .wiredWithFiles(ManifestTransformerTask::mergedManifest, ManifestTransformerTask::updatedManifest)
                .toTransform(SingleArtifact.MERGED_MANIFEST)

        

/*
* GitVersionPlugin, 注册 []ManifestUpdater 任务,其依赖了 ManifestTransformerTask。
*
* 当执行 ./gradlew :app:stagingManifestUpdater;
* 发现 其依赖了 gitVersionProvider 的 GitVersionTask;
* GitVersionTask 输出文本,并创建文件到 app/build/intermediates/gitVersionProvider/output;
* manifestUpdater 的 ManifestTransformerTask, 读取 gitVersion 文本;
* 在 manifest 文件中替换文本,得到了 app/build/intermediates/merged_manifest/staging/[build variant]ManifestUpdater/AndroidManifest.xml
*/
有意思的是,AS编译器内好几个地方报红,但运行是正常的。

在android application 模块的 build.gradle.kts 中应用 插件:
apply<gitversion.GitVersionPlugin>()
然后在命令行执行命令
./gradlew :app:stagingManifestUpdater
最终生成:
app/build/intermediates/merged_manifest/staging/stagingManifestUpdater/AndroidManifest.xml

试了,对 app项目打了个包,发现 version code 已等于 3234了


示例源码

以上内容,对应于今天以前的提交记录。

传送门

补更: 神奇了,发现 加一段代码后, ProcessBuilder 又能运行了。
对应 git commint:processBuilder 重定错误输出流后,可以执行命令了

// 代码运行这个命令,会提示 is not a git command 。 而单独放到命令行运行,是可以的。真晕
// val processBuilder = ProcessBuilder(“git”, “rev-parse --short HEAD”)
// 这个可以正常运行
val processBuilder = ProcessBuilder(“git”, “–version”)
// 如果此属性为true,则由该对象的start()方法随后启动的子进程生成的任何错误输出将与标准输出合并,
// 以便可以使用Process.getInputStream()方法读取它们。初始值为false
processBuilder.redirectErrorStream(true)
val process = processBuilder.start()

以上是关于Android 基于Gradle 7.2+,自定义插件示例(实测7.4.2也可以运行)的主要内容,如果未能解决你的问题,请参考以下文章

Android 基于Gradle 7.4.2,自定义插件示例

Android Gradle 自定义Task 详解

Android自定义Gradle插件

Android自定义Gradle插件

Android自定义Gradle插件

Android Gradle 插件Gradle 自定义 Plugin 插件 ③ ( 自定义插件作用 | Android Gradle 插件的扩展 | 自定义 Extension 扩展 )