文章目录

  • Task5 checkDebugManifest
    • 1. input/ouput
    • 2. 核心类(CheckManifest)
  • Task6 generateDebugBuildConfig
    • 1. input/output
    • 2. 核心类(GenerateBuildConfig)
  • Task7 prepareLintJar
    • 1. input/ouput
    • 2. 核心类(PrepareLintJar)
  • Task8 mainApkListPersistenceDebug
    • 1. input/output
    • 2. 核心类(MainApkListPersistence)

Task5 checkDebugManifest

1. input/ouput

taskName:checkDebugManifest
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/check-manifest/debug

2. 核心类(CheckManifest)

@TaskAction
void check() {if (!isOptional && manifest != null && !manifest.isFile()) {throw new IllegalArgumentException(String.format("Main Manifest missing for variant %1$s. Expected path: %2$s",getVariantName(), getManifest().getAbsolutePath()));}
}

该任务是校验操作,就是校验清单文件的合法性,不合法则中断任务,抛出异常

配置阶段操作

@Override
public void execute(@NonNull CheckManifest checkManifestTask) {//1. 设置任务实现类scope.getTaskContainer().setCheckManifestTask(checkManifestTask);//2. 设置相关入参checkManifestTask.setVariantName(scope.getVariantData().getVariantConfiguration().getFullName());checkManifestTask.setOptional(isManifestOptional);checkManifestTask.manifest =scope.getVariantData().getVariantConfiguration().getMainManifest();//3. 出参目录配置checkManifestTask.fakeOutputDir =new File(scope.getGlobalScope().getIntermediatesDir(),"check-manifest/" + scope.getVariantConfiguration().getDirName());
}

依赖任务:preBuildTask

Task6 generateDebugBuildConfig

BuildConfig.java想必大家非常熟悉,该任务就是生成它

1. input/output

taskName:generateDebugBuildConfig
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/generated/source/buildConfig/debug
========================END==============================

2. 核心类(GenerateBuildConfig)

@TaskAction
void generate() throws IOException {//1. 清理操作   // must clear the folder in case the packagename changed, otherwise,// there'll be two classes.File destinationDir = getSourceOutputDir();FileUtils.cleanOutputDir(destinationDir);//2. 构建一个BuildConfig生成器,用于生成BuildConfig.java    BuildConfigGenerator generator = new BuildConfigGenerator(getSourceOutputDir(),getBuildConfigPackageName());// Hack (see IDEA-100046): We want to avoid reporting "condition is always true"// from the data flow inspection, so use a non-constant value. However, that defeats// the purpose of this flag (when not in debug mode, if (BuildConfig.DEBUG && ...) will// be completely removed by the compiler), so as a hack we do it only for the case// where debug is true, which is the most likely scenario while the user is looking// at source code.//map.put(PH_DEBUG, Boolean.toString(mDebug));//3. 给生成器对象配置6个固定字段// DEBUG、APPLICATION_ID、BUILD_TYPE、FLAVOR、VERSION_CODE、VERSION_NAME   generator.addField("boolean","DEBUG",isDebuggable() ? "Boolean.parseBoolean(\"true\")" : "false").addField("String", "APPLICATION_ID", '"' + appPackageName.get() + '"').addField("String", "BUILD_TYPE", '"' + getBuildTypeName() + '"').addField("String", "FLAVOR", '"' + getFlavorName() + '"').addField("int", "VERSION_CODE", Integer.toString(getVersionCode())).addField("String", "VERSION_NAME", '"' + Strings.nullToEmpty(getVersionName()) + '"').addItems(getItems());//4. getItems,则是我们在build.gradle自定义的字段属性值//5. 生成flavor字段List<String> flavors = getFlavorNamesWithDimensionNames();int count = flavors.size();if (count > 1) {for (int i = 0; i < count; i += 2) {generator.addField("String", "FLAVOR_" + flavors.get(i + 1), '"' + flavors.get(i) + '"');}}generator.generate();}

在build.gralde -> defaultConfig 添加

buildConfigField("String", "name", "\"xiaobaoyihao\"")

执行

./gradlew generateDebugBuildConfig

看下BuildConfig.java结构

/*** Automatically generated file. DO NOT MODIFY*/
package com.gradle.task.demo;public final class BuildConfig {public static final boolean DEBUG = Boolean.parseBoolean("true");public static final String APPLICATION_ID = "com.gradle.task.demo";public static final String BUILD_TYPE = "debug";public static final String FLAVOR = "";public static final int VERSION_CODE = 1;public static final String VERSION_NAME = "1.0";// Fields from default config.public static final String name = "xiaobaoyihao";
}

可以看到自定义属性字段成功写入到BuildConfig.java类中了;至于类生成具体实现细节是在generate方法中,主要使用了文件输出流配合JavaWriter类操作来协调完成的,有兴趣自行研究

//BuildConfigGenerator.java
public void generate() throws IOException {File pkgFolder = getFolderPath();if (!pkgFolder.isDirectory()) {if (!pkgFolder.mkdirs()) {throw new RuntimeException("Failed to create " + pkgFolder.getAbsolutePath());}}File buildConfigJava = new File(pkgFolder, BUILD_CONFIG_NAME);Closer closer = Closer.create();try {FileOutputStream fos = closer.register(new FileOutputStream(buildConfigJava));OutputStreamWriter out = closer.register(new OutputStreamWriter(fos, Charsets.UTF_8));JavaWriter writer = closer.register(new JavaWriter(out));//1. 生成文件头注释说明文案writer.emitJavadoc("Automatically generated file. DO NOT MODIFY").emitPackage(mBuildConfigPackageName).beginType("BuildConfig", "class", PUBLIC_FINAL);//2. 就是上面提到的6个固定字段属性for (ClassField field : mFields) {emitClassField(writer, field);}//3. 自定义属性字段for (Object item : mItems) {if (item instanceof ClassField) {emitClassField(writer, (ClassField) item);} else if (item instanceof String) {writer.emitSingleLineComment((String) item);}}writer.endType();} catch (Throwable e) {throw closer.rethrow(e);} finally {closer.close();}
}

Task7 prepareLintJar

1. input/ouput

taskName:prepareLintJar
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/lint_jar/global/prepareLintJar/lint.jar

2. 核心类(PrepareLintJar)

/*** Task that takes the configuration result, and check that it's correct.** <p>Then copies it in the build folder to (re)publish it. This is not super efficient but because* publishing is done at config time when we don't know yet what lint.jar file we're going to* publish, we have to do this.*/
public class PrepareLintJar extends DefaultTask {...@TaskActionpublic void prepare() throws IOException {// there could be more than one files if the dependency is on a sub-projects that// publishes its compile dependencies. Rather than query getSingleFile and fail with// a weird message, do a manual check//1. 获取项目中依赖的自定义lint规则jar,此处gradle插件对其做了限制,只能有一个lint.jar超过一个会build失败Set<File> files = lintChecks.getFiles();if (files.size() > 1) {throw new RuntimeException("Found more than one jar in the '"+ VariantDependencies.CONFIG_NAME_LINTCHECKS+ "' configuration. Only one file is supported. If using a separate Gradle project, make sure compilation dependencies are using compileOnly");}//2. 如果项目中没有自定义lint规则,则清理文件,否则copyt lint.jar到指定目录if (files.isEmpty()) {if (outputLintJar.isFile()) {FileUtils.delete(outputLintJar);}} else {FileUtils.mkdirs(outputLintJar.getParentFile());Files.copy(Iterables.getOnlyElement(files), outputLintJar);}}//ConfigAction.java@Overridepublic void execute(@NonNull PrepareLintJar task) {   //指定输出位置task.outputLintJar =scope.getArtifacts().appendArtifact(InternalArtifactType.LINT_JAR, task, FN_LINT_JAR);//读取自定义规则文件集合                task.lintChecks = scope.getLocalCustomLintChecks();}
}

从上可以看出这个任务就是copy功能,lint.jar不能超过1个,也就是说如果项目中存在混合语言时,如果你要对其进行各自自定义一套check规则(每套规则会生成一个lint.jar),那对不起,执行到这任务直接中断,这应该算Android插件一个缺陷吧,高版本已修复;

关于如何自定义check规则,可以参考如下官网相关链接

https://developer.android.com/studio/write/lint?hl=zh-cn
https://github.com/googlesamples/android-custom-lint-rules.git
http://tools.android.com/tips/lint-custom-rules

Task8 mainApkListPersistenceDebug

1. input/output

taskName:mainApkListPersistenceDebug
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/apk_list/debug/mainApkListPersistenceDebug/apk-list.gson

格式化后的apk-list.gson

[{"type": "MAIN","splits": [],"versionCode": 1,"versionName": "1.0","enabled": true,"outputFile": "app-debug.apk","fullName": "debug","baseName": "debug"}
]

这个任务其实就是生成一个apk信息的gson文件,看下核心入口代码

2. 核心类(MainApkListPersistence)

这个类非常简短,我直接全部贴出来吧,有意思的是它是kotlin写的

/*** Task to persist the {@see OutputScope#apkdatas} which allows downstream tasks to depend* on the {@see InternalArtifactType#APK_LIST} rather than on various complicated data structures.* This also allow to record the choices made during configuration time about what APKs will be* produced and which ones are enabled.*/
open class MainApkListPersistence : AndroidVariantTask() {@get:OutputFilelateinit var outputFile: Fileprivate set@get:Inputlateinit var apkData : Collection<ApkData>private set@TaskActionfun fullTaskAction() {//1. 清理操作FileUtils.deleteIfExists(outputFile)//2. 返回一个apk信息的字符串val apkDataList = ExistingBuildElements.persistApkList(apkData)//3. 写入文件FileUtils.createFile(outputFile, apkDataList)}class ConfigAction(val scope: VariantScope) :TaskConfigAction<MainApkListPersistence> {override fun getName() = scope.getTaskName("mainApkListPersistence")override fun getType() = MainApkListPersistence::class.javaoverride fun execute(task: MainApkListPersistence) {task.variantName = scope.fullVariantNametask.apkData = scope.outputScope.apkDatastask.outputFile = scope.artifacts.appendArtifact(InternalArtifactType.APK_LIST,task,SdkConstants.FN_APK_LIST)}}
}

可以看到关键代码其实是在第二部操作中,我们看看内部如何实现的,

//ExistingBuildElements.kt
@JvmStatic
fun persistApkList(apkInfos: Collection<ApkInfo>): String {val gsonBuilder = GsonBuilder()gsonBuilder.registerTypeHierarchyAdapter(ApkInfo::class.java, ApkInfoAdapter())val gson = gsonBuilder.create()return gson.toJson(apkInfos)
}//ApkInfoAdapter.kt
override fun write(out: JsonWriter, value: ApkInfo?) {if (value == null) {out.nullValue()return}out.beginObject()out.name("type").value(value.type.toString())out.name("splits").beginArray()for (filter in value.filters) {out.beginObject()out.name("filterType").value(filter.filterType)out.name("value").value(filter.identifier)out.endObject()}out.endArray()out.name("versionCode").value(value.versionCode.toLong())if (value.versionName != null) {out.name("versionName").value(value.versionName)}out.name("enabled").value(value.isEnabled)if (value.filterName != null) {out.name("filterName").value(value.filterName)}if (value.outputFileName != null) {out.name("outputFile").value(value.outputFileName)}out.name("fullName").value(value.fullName)out.name("baseName").value(value.baseName)out.endObject()
}

json字符串的构造其实是依赖于goole自定义的ApkInfoAdapter适配器类来实现的,具体不说啦。
今天就到这吧。。。

Android构建流程——篇三相关推荐

  1. Android构建流程——篇二

    文章目录 预操作 任务列表 如何查看一个task类 Task1: checkDebugClasspath 1. input/output 2. 如何找到任务实现类 3. 核心类(AppClasspat ...

  2. Android构建流程——篇一

    Android构建流程 前言 APK 构建流程 AGP(3.2.0)任务列表总览图 参考文献 前言 大家平时开发Android项目时一般都是点击AS run按钮,这样apk会自动安装到手机上,这整个过 ...

  3. Android构建流程——篇四

    文章目录 Task9 generateDebugResValues 1. input/ouput 2. 核心类(GenerateResValues) Task10 generateDebugResou ...

  4. Android构建流程——篇七

    文章目录 Task24 transformClassesWithDexBuilderForDebug 1.input/ouput 2. 核心类(DexArchiveBuilderTransform,T ...

  5. Android构建流程——篇六

    文章目录 Task17: javaPreCompileDebug 1. input/ouput 2. 验证 3. 核心类(JavaPreCompileTask) Task18:compileDebug ...

  6. Android构建流程——篇五

    文章目录 Task13: processDebugManifest 1. input/ouput 2. 整体流程 3. 调用链路 4. 核心类(MergeManifests) 5. AndroidBu ...

  7. Android构建流程——篇八

    文章目录 Task29 checkDebugLibraries 1. inut/ouput 2. 核心类(CheckMultiApkLibrariesTask) Task30 processDebug ...

  8. Android日志[进阶篇]三-Logcat 命令行工具

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  9. Android OpenCV应用篇三:提取图片中的文字

    上篇我们大致了解了如何运用OpenCV在Android上进行图片但简单处理 Android OpenCV应用篇二:图片处理 接下来我们就运用之前的一些相关技术来搞点事情: 如何从一张图片中将文字提取出 ...

最新文章

  1. mysql密码设置 alert_MySQL用户、权限及密码操作
  2. NBT:牛瘤胃微生物组的参考基因组集
  3. 关于Debug和Release编译方式
  4. Intel 酷睿i5 6300HQ与Intel 酷睿i7 6700HQ哪个好
  5. 【GNN】硬核!一文梳理经典图网络模型
  6. mybatis入门基础(四)----输入映射和输出映射
  7. 数论基础之组合数计数问题
  8. SecureCRT或XShell软件
  9. go - range
  10. iOS9新系统下App Store应用上传新指南
  11. python 切割_Python切割九宫格图
  12. Google Cloud资源层级, IAM Identity and Access Management, 控制台云交互
  13. Keil(C51)的安装与注册
  14. 关于打印室计算机购置的申请书,物资采购申请书格式范文
  15. Linux网络服务之常规vsFTP服务配置(超级详细图解一步骤一图)
  16. 手机和工业计算机运算能力对比,手机cpu和电脑cpu差距有多大_手机cpu跟电脑cpu差多少...
  17. 即将发布的 Apache Spark 3.2 将内置 Pandas API
  18. Z39.50客户端源代码(C#)
  19. 数据库SQL实战11_获取员工其当前的薪水比其manager当前薪水还高的相关信息,当前表示to_date=‘9999-01-01‘, 结果第一列给出员工的emp_no, 第二列给出其manager
  20. 创建银行账户,实现存款,取款,转账(正解)

热门文章

  1. 单调不减序列查询第一个大于等于_[力扣84,85] 单调栈
  2. 十一、Grafana监控系统
  3. 四十一、Linux基础命令,用户管理和文件系统总结
  4. 三十、深入Python中的Pickle和Json模块
  5. 化工原理 蒸馏(上)
  6. 【Python金融量化 5- 100 】、五、蒙特卡洛和毛利
  7. 史上最全推荐系统传统算法合集
  8. 浅谈数据中的偏差问题和推荐系统去偏最新研究进展
  9. 语音合成:模拟最像人类声音的系统
  10. RealFormer:把残差转移到Attention矩阵上面去