Public Artifact
支持的组件输出、输入类型
- FILE:文件
- DIRECTORY:目录
SingleArtifact
支持的转换类型:(列举几个常用的)
- APK:安装包路径
- MERGED_MANIFEST:Manifest 文件
- OBFUSCATION_MAPPING_FILE:mapping.txt 文件
- AAR:aar 包
- ASSETS:assets 文件
- MERGED_NATIVE_LIBS:so 库文件
1. 立刻终止构建
AGP7+
之前我是这样直接抛出异常,还是比较快终止构建,到 AGP8 之后发现不行了,默认开启即使抛出异常也会继续执行剩下的 Task
class ExceptionHelper {
companion object {
@JvmStatic
fun throwExecution(msg: String) {
throw GradleException(msg)
System.exit(-1)
}
}
}
AGP8+
可以在插件初始化时设置
//抛出异常时立刻停止构建,其实 AGP 低版本也有这个
project.gradle.startParameter.isContinueOnFailure = false
2. Manifest 合并
AGP+7
- 获取所有 Task
project.tasks.findAll().iterator()
- 遍历、判断 Task name,拿到特定任务(如 processReleaseManifest、mergeReleaseAssets)
- 为特定 Task 添加前后执行逻辑(设置 doFirst 、doLast)
project.afterEvaluate {
expendTask()
}
private List expendTask() {
Iterator<Task> eIterator = project.tasks.findAll().iterator()
while (eIterator.hasNext()) {
Task theTask = eIterator.next()
switch (CommUtils.judgeTaskType(theTask.name)) {
case TaskType.TASK_PROCESS_RELEASE_MANIFEST:
theTask.doLast {
}
break
}
}
}
AGP8+
- 指定输入:
@get:InputFile
- 指定输出:
@get:OutputFile
(一定要输出,否则下一个 Transform 没有输入) - 指定 SingleArtifact:
SingleArtifact.MERGED_MANIFEST
private fun registerTransform(project: Project) {
val androidExt = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidExt.onVariants { variant ->
//处理 manifest 文件
val manifestUpdater = project.tasks.register(
variant.name + "ManifestTransformerTask", ManifestTransformerTask::class.java
) {}
variant.artifacts.use(manifestUpdater).wiredWithFiles(
ManifestTransformerTask::mergedManifest,
ManifestTransformerTask::updatedManifest
).toTransform(SingleArtifact.MERGED_MANIFEST)
}
}
abstract class ManifestTransformerTask : DefaultTask() {
@get:InputFile
@get:PathSensitive(PathSensitivity.NONE)
abstract val mergedManifest: RegularFileProperty
@get:OutputFile
abstract val updatedManifest: RegularFileProperty
@TaskAction
fun taskAction() {
println("ManifestTransformerTask run")
var manifest = mergedManifest.asFile.get().readText()
//todo:解析 manifest 并做相应修改或查找,最后输出更新
updatedManifest.get().asFile.writeText(manifest)
}
}
3. Assets 合并
AGP7+
project.afterEvaluate {
expendTask()
}
private List expendTask() {
Iterator<Task> eIterator = project.tasks.findAll().iterator()
while (eIterator.hasNext()) {
Task theTask = eIterator.next()
switch (CommUtils.judgeTaskType(theTask.name)) {
case TaskType.TASK_MERGE_RELEASE_ASSETS:
theTask.doLast {
}
break
}
}
}
AGP8+
- 指定输入:
@get:inputDir
- 指定输出:
@get:outputDir
(一定要输出,否则下一个 Transform 没有输入) - 指定 SingleArtifact:
SingleArtifact.ASSETS
private fun registerTransform(project: Project) {
project.plugins.withType(AppPlugin::class.java) {
val appExt =
project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
appExt.onVariants { variant ->
//assets merge
val assetsTaskProvider =
project.tasks.register(
variant.name + ConstantStr.PREFIX_TRANSFORM + "AssetsTransformerTask",
AssetsTransformerTask::class.java
)
variant.artifacts.use(assetsTaskProvider)
.wiredWithDirectories(
AssetsTransformerTask::inputDir,
AssetsTransformerTask::outputDir
).toTransform(SingleArtifact.ASSETS)
}
}
}
注意点:
- transform 前清空输出目录:
PluginFileUtils.deleteFolder(outFileDir)
- assets 路径变更:
- AGP6+:build\intermediates\
merged_assets
- AGP7+、8+:build\intermediates\
assets
\release\mergeReleaseAssets
- AGP6+:build\intermediates\
abstract class AssetsTransformerTask : DefaultTask() {
@get:InputDirectory
abstract val inputDir: DirectoryProperty
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
@TaskAction
fun taskAction() {
val outFileDir = outputDir.asFile.get()
if (outFileDir.exists()) {
//否则报错:文件已存在(不 clean build 目录情况下)
CmdColorPrintUtils.outputYellow("重新打包前需要先清空目录:\n\t ${outFileDir.absolutePath}")
PluginFileUtils.deleteFolder(outFileDir)
}
// We must copy the contents of the input directory to the output directory before our transformation
inputDir.get().asFile.copyRecursively(outputDir.get().asFile)
println("AssetsTransformerTask outputDir: ${outputDir.asFile.get().absolutePath}")
outputDir.asFileTree.forEach {
println("AssetsTransformerTask output =${it.absolutePath}")
}
}
}
如果注册了 assets Transform,那么 assets 输出目录下会有多个目录:
- mergeReleaseAssets:系统默认名称
- 其他自定义名称:例如我插件的 releaseDnAssetsTransformerTask
4. Native so 合并
了解到
- SingleArtifact.ASSETS:有 Transformable 接口,说明可以使用 toTransform 进行转换
- SingleArtifact.MERGED_NATIVE_LIBS:只有 SingleArtifact(DIRECTORY),没有 Transformable,那么就不能使用 toTransform 方式处理了
以下是针对 AGP8+ 处理方式:我们以为删除不必要的 abi 目录为例
- 获取 MERGED_NATIVE_LIBS 输入目录
private fun registerTransform(project: Project) {
project.plugins.withType(AppPlugin::class.java) {
val appExt =
project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
appExt.onVariants { variant ->
CheckPluginManager.getInstance().mergedNativeLibsInput =
variant.artifacts.get(SingleArtifact.MERGED_NATIVE_LIBS)
}
}
- 监听 task 依赖构建
- 正常匹配找到
mergeReleaseNativeLibs
Task 并设置 doLast(设置 doFirst 测试似乎不起作用)
private fun setBuildListener(project: Project) {
project.gradle.taskGraph.addTaskExecutionGraphListener { graph ->
val allTasks = graph.allTasks as List<Task>
if (allTasks.isNotEmpty()) {
allTasks.forEach { task ->
val isMergeLibsTask = task.name.matches(Regex("^merge.*NativeLibs\$"))
if (isMergeLibsTask) {
task.doLast {
processMergeNativeLibs()
}
}
}
}
}
}
//定义所有的 abi 目录,为例找到所在目录路径
val mAbiList = listOf(
"arm64-v8a",
"armeabi",
"armeabi-v7a",
"x86",
"x86_64"
)
private fun processMergeNativeLibs() {
//定义需要保留了 abi,比如我需要保留 arm64-v8a
val abiList = CheckPluginManager.getInstance().abiList
if (abiList.isEmpty()) {
return
}
val libDir =
CheckPluginManager.getInstance().mergedNativeLibsInput.get().asFile
val abiDir: File = findAbiDir(libDir) ?: return
CmdColorPrintUtils.outputGreen("重置 ABI:")
abiDir.listFiles()?.forEach {
if (!abiList.contains(it.name)) {
PluginFileUtils.deleteFolder(it)
CmdColorPrintUtils.outputGreen("\t移除 ${it.name}")
}
}
}
private fun findAbiDir(dir: File): File? {
if (dir.isDirectory) {
val files = dir.listFiles()
if (files != null && files.isNotEmpty()) {
val firstFile = files[0]
return if (mAbiList.contains(firstFile?.name)) {
dir
} else {
findAbiDir(firstFile)
}
}
}
return null
}