Gradle Receipes (AGP-7.3) & AGP 使用指南

Posted 川峰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gradle Receipes (AGP-7.3) & AGP 使用指南相关的知识,希望对你有一定的参考价值。

本文所有代码是基于 https://github.com/android/gradle-recipes agp-7.3分支版本的官方 sample。由于缺乏清晰易懂的说明文档(目前我没有找到可读性较好的文档,如果你知道,请留言告知),有些sample代码即便能够跑通,但在细节方便仍然不是很容易让人理解。

从 AGP 7.0 开始,AGP的API相较AGP 7.0 以前的变化较大,且迭代更新比较频繁,目前官方最新已迭代至8.0,请注意对照你所使用的AGP版本。但是据官方的说法是,从 AGP 7.0+之后的API将逐渐趋于稳定,你不用担心升级之后API会变化太大而导致不能使用了。(我信你个鬼)

另外Gradle插件和android Studio的版本之间存在着兼容要求,请参考这里

我的电脑目前使用的Android Studio的版本是小海豚,所以AGP的要求是7.3,Gradle的要求是至少7.4:

Groovy Use

获取所有Class文件

import org.gradle.api.DefaultTask
import org.gradle.api.file.Directory
import org.gradle.api.file.RegularFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.TaskAction
import org.gradle.api.provider.ListProperty
import com.android.build.api.artifact.MultipleArtifact

abstract class GetAllClassesTask extends DefaultTask 

    @InputFiles
    abstract ListProperty<Directory> getAllClasses()

    @InputFiles
    abstract ListProperty<RegularFile> getAllJarsWithClasses()

    @TaskAction
    void taskAction() 
        allClasses.get().forEach  directory ->
            println("Directory : $directory.asFile.absolutePath")
            directory.asFile.traverse(type: groovy.io.FileType.FILES)  file ->
                println("File : $file.absolutePath")
            
            allJarsWithClasses.get().forEach  file ->
                println("JarFile : $file.asFile.absolutePath")
            
        
    



androidComponents 
    onVariants(selector().all(),  variant ->
        project.tasks.register(variant.getName() + "GetAllClasses", GetAllClassesTask.class) 
            it.allClasses.set(variant.artifacts.getAll(MultipleArtifact.ALL_CLASSES_DIRS.INSTANCE))
            it.allJarsWithClasses.set(variant.artifacts.getAll(MultipleArtifact.ALL_CLASSES_JARS.INSTANCE))

        
    )


执行任务后输出:

修改Class文件

在根目录build.gradle中引入javassist插件:

buildscript 
    dependencies 
        classpath("org.javassist:javassist:3.22.0-GA")
    

plugins 
    id 'com.android.application' version '7.3.1' apply false
    id 'com.android.library' version '7.3.1' apply false

app/build.gradle中添加任务:

import org.gradle.api.DefaultTask;
import org.gradle.api.file.Directory;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.TaskAction;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.io.FileInputStream;

abstract class ModifyClassesTask extends DefaultTask 

    @InputFiles
    abstract ListProperty<Directory> getAllClasses();

    @OutputFiles
    abstract DirectoryProperty getOutput();

    @TaskAction
    void taskAction() 
        ClassPool pool = new ClassPool(ClassPool.getDefault());
        allClasses.get().forEach  directory ->
            System.out.println("Directory : $directory.asFile.absolutePath");
            directory.asFile.traverse(type: groovy.io.FileType.FILES)  file ->
                System.out.println(file.absolutePath)
                if (file.name == "SomeSource.class") 
                    System.out.println("File : $file.absolutePath")
                    CtClass interfaceClass = pool.makeInterface("com.android.api.tests.SomeInterface");
                    System.out.println("Adding $interfaceClass")
                    System.out.println("Write to $output.get().asFile.absolutePath")
                    interfaceClass.writeFile(output.get().asFile.absolutePath)
                    new FileInputStream(file).withCloseable 
                        CtClass ctClass = pool.makeClass(it)
                        ctClass.addInterface(interfaceClass)
                        CtMethod m = ctClass.getDeclaredMethod("toString")
                        if (m != null) 
                            m.insertBefore(" System.out.println(\\"Some Extensive Tracing\\"); ")
                        
                        ctClass.writeFile(output.get().asFile.absolutePath)
                    
                
            
        
    


androidComponents 
    onVariants(selector().all(),  variant ->
        TaskProvider<ModifyClassesTask> taskProvider = project.tasks.register(variant.getName() + "ModifyAllClasses", ModifyClassesTask.class)
        variant.artifacts.use(taskProvider)
                .wiredWith(  it.getAllClasses() ,  it.getOutput() )
                .toTransform(MultipleArtifact.ALL_CLASSES_DIRS.INSTANCE)
    )

新建一个 SomeSource 类:

package com.android.api.tests;

class SomeSource 
    public String toString() 
        return "Something !";
    

执行 assembleDebug 后会自动触发 debugModifyAllClasses 任务被执行

在build\\intermediates\\all_classes_dirs\\debug\\debugModifyAllClasses目录下生成一个SomeInterface.class接口类文件,同时SomeSource.class文件中实现了该接口:


添加Class文件

abstract class AddClassesTask extends DefaultTask 

    @OutputFiles
    abstract DirectoryProperty getOutput();

    @TaskAction
    void taskAction() 

        ClassPool pool = new ClassPool(ClassPool.getDefault());
        CtClass interfaceClass = pool.makeInterface("com.android.api.tests.SomeInterface2")
        System.out.println("Adding $interfaceClass")
        interfaceClass.writeFile(output.get().asFile.absolutePath)
    


androidComponents 
    onVariants(selector().all(),  variant ->
        TaskProvider<AddClassesTask> taskProvider = project.tasks.register(variant.getName() + "AddAllClasses", AddClassesTask.class)
        variant.artifacts.use(taskProvider)
                .wiredWith(  it.getOutput() )
                .toAppendTo(MultipleArtifact.ALL_CLASSES_DIRS.INSTANCE)
    )

执行 assembleDebug 后会自动触发 debugAddAllClasses 任务,在 build\\intermediates\\all_classes_dirs\\debug\\debugAddAllClasses 目录下生成一个接口类文件SomeInterface2.class.

获取APK文件

import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.variant.BuiltArtifacts
import com.android.build.api.variant.BuiltArtifactsLoader

abstract class DisplayApksTask extends DefaultTask 

    @InputFiles
    abstract DirectoryProperty getApkFolder()

    @Internal
    abstract Property<BuiltArtifactsLoader> getBuiltArtifactsLoader()

    @TaskAction
    void taskAction() 
        BuiltArtifacts artifacts = getBuiltArtifactsLoader().get().load(getApkFolder().get())
        if (artifacts == null) 
            throw new RuntimeException("Cannot load APKs")
        
        artifacts.elements.forEach 
            println("Got an APK at $it.outputFile")
        
    


androidComponents 
    onVariants(selector().all(),  variant ->
        project.tasks.register(variant.getName() + "DisplayApks", DisplayApksTask.class) 
            it.apkFolder.set(variant.artifacts.get(SingleArtifact.APK.INSTANCE))
            it.builtArtifactsLoader.set(variant.artifacts.getBuiltArtifactsLoader())
        
    )


执行 debugDisplayApks 任务,输出:

修改 ApplicationId

abstract class ApplicationIdProducerTask extends DefaultTask 

    @OutputFile
    abstract RegularFileProperty getOutputFile()

    @TaskAction
    void taskAction() 
        getOutputFile().get().getAsFile().write("set.from.task." + name)
    


androidComponents 
    onVariants(selector().withBuildType("debug"))  variant ->
        TaskProvider appIdProducer = tasks.register(variant.name + "AppIdProducerTask", ApplicationIdProducerTask.class)  task ->
            File outputDir = new File(getBuildDir(), task.name)
            println("outputDir: $outputDir.absolutePath")
            task.getOutputFile().set(new File(outputDir, "appId.txt"))

        
        variant.setApplicationId(appIdProducer.flatMap  task ->
            task.getOutputFile().map  it.getAsFile().text 
        )
    

执行 assembleDebug 任务,会自动执行 debugAppIdProducerTask 任务,然后运行app,发现BuildConfig.APPLICATION_ID和context.getPackageName() 返回的都是set.from.task.debugAppIdProducerTask。

重写 Manifest 文件

abstract class GitVersionTask extends DefaultTask 

    @OutputFile
    abstract RegularFileProperty getGitVersionOutputFile()

    @TaskAction
    void taskAction() 
        // this would be the code to get the tip of tree version,
        // String gitVersion = "git rev-parse --short HEAD".execute().text.trim()
        // if (gitVersion.isEmpty()) 
        //    gitVersion="12"
        //
        getGitVersionOutputFile().get().asFile.write("1234")
    


abstract class ManifestProducerTask extends DefaultTask 
    @InputFile
    abstract RegularFileProperty getGitInfoFile()

    @OutputFile
    abstract RegularFileProperty getOutputManifest()

    @TaskAction
    void taskAction() 
        String manifest = """<?xml version="1.0" encoding="utf-8"?>
                <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                    package="com.android.build.example.minimal"
                    android:versionName="$new String(getGitInfoFile().get().asFile.readBytes())"
                    android:versionCode="1" >

                    <application android:label="Minimal">
                        <activity android:name="MainActivity">
                            <intent-filter>
                                <action android:name="android.intent.action.MAIN" />
                                <category android:name="android.intent.category.LAUNCHER" />
                            </intent-filter>
                        </activity>
                    </application>
                </manifest>
                    """
        println("Writes to " + getOutputManifest().get().getAsFile().getAbsolutePath())
        getOutputManifest().get().getAsFile().write(manifest)
    


androidComponents 
    onVariants(selector().all(), 
        TaskProvider gitVersionProvider = tasks.register(it.getName() + 'GitVersionProvider', GitVersionTask) 
            task ->
                task.gitVersionOutputFile.set(
                        new File(project.buildDir, "intermediates/gitVersionProvider/output")
                )
                task.outputs.upToDateWhen  false 
        

        TaskProvider manifestProducer = tasks.register(it.getName() + 'ManifestProducer', ManifestProducerTask) 
            task ->
                task.gitInfoFile.set(gitVersionProvider.flatMap  it.getGitVersionOutputFile() )
        
        it.artifacts.use(manifestProducer)
                .wiredWith( it.outputManifest )
                .toCreate(SingleArtifact.MERGED_MANIFEST.INSTANCE)
    )


执行 assembleDebug 后会自动触发 debugManifestProducer 任务,而 debugManifestProducer 任务的输入依赖于 debugGitVersionProvider 的输出,因此 debugGitVersionProvider 任务先被执行,在 build/intermediates/gitVersionProvider/output 文件中生成版本号。然后会执行 debugManifestProducer 任务,在 build\\intermediates\\merged_manifest\\debug\\debugManifestProducer 目录下生成被覆写的AndroidManifest.xml 文件,其中android:versionName的值被替换为上面output 文件中生成的版本号:

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android.build.example.minimal"
        android:versionName="1234"
        android:versionCode="1" >
        <application android:label="Minimal">
            <activity android:name="MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    </manifest>

修改 Manifest 文件内容

abstract class GitVersionTask extends DefaultTask 

    @OutputFile
    abstract RegularFileProperty getGitVersionOutputFile()

    @TaskAction
    void taskAction() 
        // this would be the code to get the tip of tree version,
        // String gitVersion = "git rev-parse --short HEAD".execute().text.trim()
        // if (gitVersion.isEmpty()) 
        //    gitVersion="12"
        //
        getGitVersionOutputFile().get().asFile.write("8888")
    


abstract class ManifestTransformerTask extends DefaultTask 

    @InputFile
    abstract RegularFileProperty getGitInfoFile()

    @InputFile
    abstract RegularFileProperty getMergedManifest()

    @OutputFile
    abstract RegularFileProperty getUpdatedManifest()

    @TaskAction
    void taskAction() 
        String gitVersion = new String以上是关于Gradle Receipes (AGP-7.3) & AGP 使用指南的主要内容,如果未能解决你的问题,请参考以下文章

gradle sts 与 gradle 有何区别

简单理解Gradle,Gradle是什么,一分钟入门Gradle

GroovyGradle 环境搭建 ( 下载 Gradle 工具 | 查找本地缓存的 Gradle 工具 | 配置 Gradle 环境变量 )

Android Gradle 插件Gradle 构建工具简介 ① ( Gradle 环境配置 | 官网下载 Gradle 软件包 | 在本地用户目录下查找 | 配置 Gradle 环境变量 )

Android Gradle 插件Gradle 依赖管理 ① ( org.gradle.api.Project 配置 | Android Gradle 插件配置与 Gradle 配置关联 ) ★

gradle wrapper, gradle ,gradle plugin 之间的关系