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,Gradle是什么,一分钟入门Gradle
GroovyGradle 环境搭建 ( 下载 Gradle 工具 | 查找本地缓存的 Gradle 工具 | 配置 Gradle 环境变量 )
Android Gradle 插件Gradle 构建工具简介 ① ( Gradle 环境配置 | 官网下载 Gradle 软件包 | 在本地用户目录下查找 | 配置 Gradle 环境变量 )
Android Gradle 插件Gradle 依赖管理 ① ( org.gradle.api.Project 配置 | Android Gradle 插件配置与 Gradle 配置关联 ) ★