文章目录

  • 预操作
  • 任务列表
  • 如何查看一个task类
  • Task1: checkDebugClasspath
    • 1. input/output
    • 2. 如何找到任务实现类
    • 3. 核心类(AppClasspathCheckTask)
  • Task2: preDebugBuild
    • 1. input/ouput
    • 2. 核心类(AppPreBuildTask)
  • Task3: compileDebugAidl
    • 1. input/output
    • 2. 核心类(AidlCompile)
    • 3. 映射文件
  • Task4: compileDebugRenderscript
    • 1. RenderScript 概览
    • 2. input/ouput
    • 3. 核心类(RenderscriptCompile)
  • 参考链接

预操作

为说明Android构建过程中gradle执行的各task,写了简单demo

git clone https://github.com/xiaobaoyihao/AndroidGradleTaskDemo.git

先罗列下Android构建流程中任务有哪些,本系列讲解都是基于gradle plugin 3.2.0源码

任务列表

clone下demo,终端执行

./gradlew assembleDebug --console=plain

输出如下任务链

:app:checkDebugClasspath UP-TO-DATE
:app:preBuild UP-TO-DATE
:app:preDebugBuild UP-TO-DATE
:app:compileDebugAidl UP-TO-DATE
:app:compileDebugRenderscript UP-TO-DATE
:app:checkDebugManifest UP-TO-DATE
:app:generateDebugBuildConfig UP-TO-DATE
:app:prepareLintJar UP-TO-DATE
:app:mainApkListPersistenceDebug UP-TO-DATE
:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources UP-TO-DATE
:app:createDebugCompatibleScreenManifests UP-TO-DATE
:app:processDebugManifest UP-TO-DATE
:app:splitsDiscoveryTaskDebug UP-TO-DATE
:app:processDebugResources UP-TO-DATE
:app:generateDebugSources UP-TO-DATE
:app:javaPreCompileDebug UP-TO-DATE
:app:compileDebugJavaWithJavac UP-TO-DATE
:app:compileDebugNdk NO-SOURCE
:app:compileDebugSources UP-TO-DATE
:app:mergeDebugShaders UP-TO-DATE
:app:compileDebugShaders UP-TO-DATE
:app:generateDebugAssets UP-TO-DATE
:app:mergeDebugAssets UP-TO-DATE
:app:transformClassesWithDexBuilderForDebug UP-TO-DATE
:app:transformDexArchiveWithExternalLibsDexMergerForDebug UP-TO-DATE
:app:transformDexArchiveWithDexMergerForDebug UP-TO-DATE
:app:mergeDebugJniLibFolders UP-TO-DATE
:app:transformNativeLibsWithMergeJniLibsForDebug UP-TO-DATE
:app:checkDebugLibraries UP-TO-DATE
:app:processDebugJavaRes NO-SOURCE
:app:transformResourcesWithMergeJavaResForDebug UP-TO-DATE
:app:validateSigningDebug UP-TO-DATE
:app:packageDebug UP-TO-DATE
:app:assembleDebug UP-TO-DATE

为了更清楚观察各任务,我们可以对每个任务的输入和输出添加日志打印,在demo中放开build.gradle中任务打印区域代码

如何查看一个task类

可以通过taskname来查找,一般任务路径大部分都是在

com.android.build.gradle.internal.tasks
com.android.build.gradle.tasks

目录下,任务名称基本和类名一致

Task1: checkDebugClasspath

1. input/output

input:/Users/apple/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/2774ea4f1cf1e83a6ad8e8d3c8b463b6/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f43c0ba95b6494825ed940fc4f04662b/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/559112320064089dfaf6780e71d5b44f/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/c2c3ad4abfd49316f6769b8238b0f010/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/9ac5f97e8ccb24c52b7cbb6202c12ad0/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/53ab5ad72634f3497309a8788f3ca200/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/7e6a4ce6591d722d47aafc36d980f8b4/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/4c474caa9ac1f01c4936bd96905ecacd/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/868eaa7e0c620cd85d72ad4f340e8bb1/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/4ec3c1c46e5bad9ac3b91f45a2afec3e/jars/classes.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.support/support-annotations/26.1.0/814258103cf26a15fcc26ecce35f5b7d24b73f8/support-annotations-26.1.0.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.support.constraint/constraint-layout-solver/1.1.3/bde0667d7414c16ed62d3cfe993cff7f9d732373/constraint-layout-solver-1.1.3.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/5b2333922ba05b1f174de51739b24d14/jars/classes.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/android.arch.lifecycle/common/1.0.0/e414a4cb28434e25c4f6aa71426eb20cf4874ae9/common-1.0.0.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/android.arch.core/common/1.0.0/a2d487452376193fc8c103dd2b9bd5f2b1b44563/common-1.0.0.jar
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/checkDebugClasspath/debug

从task字面可以猜测出该任务是对app classpath做校验,具体是在那个类呢?
我们找到到AppClasspathCheckTask.java,如何确定是正确的呢?
我们发现该类中有个ConfigAction.getName方法


其实这个方法的返回字符串就是执行的任务名(checkDebugClasspath),具体怎么流程大家直接看代码就知道了

ConfigAction被调用的地方

核心入口代码

//ApplicationTaskManager.java
@Overrideprotected Task createVariantPreBuildTask(@NonNull VariantScope scope) {final VariantType variantType = scope.getVariantConfiguration().getType();if (variantType.isApk()) {AppClasspathCheckTask classpathCheck =taskFactory.create(new AppClasspathCheckTask.ConfigAction(scope));return (variantType.isTestComponent()? taskFactory.create(new TestPreBuildTask.ConfigAction(scope)): taskFactory.create(new AppPreBuildTask.ConfigAction(scope))).dependsOn(classpathCheck);}return super.createVariantPreBuildTask(scope);}

有人会问中间的debug怎么多出来?追踪getName方法内部实现发现最终调用BaseVariantData.getTaskName方法

public String getTaskName(@NonNull String prefix, @NonNull String suffix) {return StringHelper.appendCapitalized(prefix, variantConfiguration.getFullName(), suffix);
}// VariantConfiguration.javapublic String getFullName() {if (mFullName == null) {mFullName =computeFullName(getFlavorName(),mBuildType,mType,mTestedConfig == null ? null : mTestedConfig.getType());}return mFullName;}/*** Returns the full, unique name of the variant in camel case (starting with a lower case),* including BuildType, Flavors and Test (if applicable).** @param flavorName the flavor name, as computed by {@link #computeFlavorName(List)}* @param buildType the build type* @param type the variant type* @return the name of the variant*/public static <B extends BuildType> String computeFullName(@NonNull String flavorName,@NonNull B buildType,@NonNull VariantType type,@Nullable VariantType testedType) {StringBuilder sb = new StringBuilder();if (!flavorName.isEmpty()) {sb.append(flavorName);StringHelper.appendCapitalized(sb, buildType.getName());} else {sb.append(buildType.getName());}if (type.isHybrid()) {sb.append("Feature");}if (type.isTestComponent()) {if (testedType != null && testedType.isHybrid()) {sb.append("Feature");}sb.append(type.getSuffix());}return sb.toString();}

可以看到computeFullName是返回variant相关的名字,和我们输入的assembleDebug相匹配,所以我们后期直接通过任务名就能准确找到对应ConfigAction类了这样真正的Task类名也就找到了

2. 如何找到任务实现类

确定任务名 -> 在所有TaskConfigAction的子类中寻找getName返回值是否与其task名匹配 -> 对应Task实现类

3. 核心类(AppClasspathCheckTask)

//AppClasspathCheckTask.java
@TaskAction
void run() {compareClasspaths();}//ClasspathComparisionTask.java
void compareClasspaths() {//com.android.support:appcompat-v7:23.3.0//group:module/artifact:versionSet<ResolvedArtifactResult> runtimeArtifacts = runtimeClasspath.getArtifacts();Set<ResolvedArtifactResult> compileArtifacts = compileClasspath.getArtifacts();// Store a map of groupId -> (artifactId -> versions)Map<String, Map<String, String>> runtimeIds =Maps.newHashMapWithExpectedSize(runtimeArtifacts.size());//1. 存储runtime依赖版本信息到map中groupId -> (artifactId -> versions)for (ResolvedArtifactResult artifact : runtimeArtifacts) {// only care about external dependencies to compare versions.final ComponentIdentifier componentIdentifier =artifact.getId().getComponentIdentifier();if (componentIdentifier instanceof ModuleComponentIdentifier) {ModuleComponentIdentifier moduleId =(ModuleComponentIdentifier) componentIdentifier;// get the sub-map, creating it if needed.Map<String, String> subMap =runtimeIds.computeIfAbsent(moduleId.getGroup(), s -> new HashMap<>());subMap.put(moduleId.getModule(), moduleId.getVersion());}}//对compileArtifacts集合进行遍历,并和compileArtifacts中相同的group.module比较,如何发现版本不一致,调用onDifferentVersionsFound方法for (ResolvedArtifactResult artifact : compileArtifacts) {// only care about external dependencies to compare versions.final ComponentIdentifier componentIdentifier =artifact.getId().getComponentIdentifier();if (componentIdentifier instanceof ModuleComponentIdentifier) {ModuleComponentIdentifier moduleId =(ModuleComponentIdentifier) componentIdentifier;Map<String, String> subMap = runtimeIds.get(moduleId.getGroup());if (subMap == null) {continue;}String runtimeVersion = subMap.get(moduleId.getModule());if (runtimeVersion == null) {continue;}if (runtimeVersion.equals(moduleId.getVersion())) {continue;}onDifferentVersionsFound(moduleId.getGroup(),moduleId.getModule(),runtimeVersion,moduleId.getVersion());}}}//AppClasspathCheckTask.java
@Override
void onDifferentVersionsFound(@NonNull String group,@NonNull String module,@NonNull String runtimeVersion,@NonNull String compileVersion) {//这个方法很简单,就是比较版本不一样的话,会提示依赖有冲突,运行时可能导致crashString suggestedVersion;try {GradleVersion runtime = GradleVersion.parse(runtimeVersion);GradleVersion compile = GradleVersion.parse(compileVersion);if (runtime.compareTo(compile) > 0) {suggestedVersion = runtimeVersion;} else {suggestedVersion = compileVersion;}} catch (Throwable e) {// in case we are unable to parse versions for some reason, choose runtimesuggestedVersion = runtimeVersion;}String message =String.format("Conflict with dependency '%1$s:%2$s' in project '%3$s'. Resolved versions for "+ "runtime classpath (%4$s) and compile classpath (%5$s) differ. This "+ "can lead to runtime crashes. To resolve this issue follow "+ "advice at https://developer.android.com/studio/build/gradle-tips#configure-project-wide-properties. "+ "Alternatively, you can try to fix the problem "+ "by adding this snippet to %6$s:\n"+ "dependencies {\n"+ "    implementation(\"%1$s:%2$s:%7$s\")\n"+ "}\n",group,module,getProject().getPath(),runtimeVersion,compileVersion,getProject().getBuildFile(),suggestedVersion);reporter.reportWarning(EvalIssueReporter.Type.GENERIC, message);}

简单总结:

该任务就是对编译类路径和运行时类路径进行校验,如果相同的group.module中存在不同version,则提示用户依赖有冲突,会导致运行是crash

冲突解决方案参见官网

Task2: preDebugBuild

1. input/ouput

taskName:preDebugBuild
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/2774ea4f1cf1e83a6ad8e8d3c8b463b6/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f43c0ba95b6494825ed940fc4f04662b/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/559112320064089dfaf6780e71d5b44f/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/c2c3ad4abfd49316f6769b8238b0f010/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/9ac5f97e8ccb24c52b7cbb6202c12ad0/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/53ab5ad72634f3497309a8788f3ca200/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/7e6a4ce6591d722d47aafc36d980f8b4/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/4c474caa9ac1f01c4936bd96905ecacd/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/868eaa7e0c620cd85d72ad4f340e8bb1/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/4ec3c1c46e5bad9ac3b91f45a2afec3e/AndroidManifest.xml
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/5b2333922ba05b1f174de51739b24d14/AndroidManifest.xml
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/prebuild/debug

输入都为依赖库的清单文件,输出为空;这个任务简单说也是对应用变体做校验

2. 核心类(AppPreBuildTask)

@TaskAction
void run() {Set<ResolvedArtifactResult> compileArtifacts = new HashSet<>();compileArtifacts.addAll(compileManifests.getArtifacts());compileArtifacts.addAll(compileNonNamespacedManifests.getArtifacts());Set<ResolvedArtifactResult> runtimeArtifacts = new HashSet<>();runtimeArtifacts.addAll(runtimeManifests.getArtifacts());runtimeArtifacts.addAll(runtimeNonNamespacedManifests.getArtifacts());// create a map where the key is either the sub-project path, or groupId:artifactId for// external dependencies.// For external libraries, the value is the version.Map<String, String> runtimeIds = Maps.newHashMapWithExpectedSize(runtimeArtifacts.size());// build a list of the runtime artifactsfor (ResolvedArtifactResult artifact : runtimeArtifacts) {handleArtifact(artifact.getId().getComponentIdentifier(), runtimeIds::put);}// run through the compile ones to check for provided only.for (ResolvedArtifactResult artifact : compileArtifacts) {final ComponentIdentifier compileId = artifact.getId().getComponentIdentifier();handleArtifact(compileId,(key, value) -> {//校验代码逻辑String runtimeVersion = runtimeIds.get(key);if (runtimeVersion == null) {if (isBaseModule) {String display = compileId.getDisplayName();throw new RuntimeException("Android dependency '"+ display+ "' is set to compileOnly/provided which is not supported");}} else if (!runtimeVersion.isEmpty()) {// compare versions.if (!runtimeVersion.equals(value)) {throw new RuntimeException(String.format("Android dependency '%s' has different version for the compile (%s) and runtime (%s) classpath. You should manually set the same version via DependencyResolution",key, value, runtimeVersion));}}});}
}private void handleArtifact(@NonNull ComponentIdentifier id, @NonNull BiConsumer<String, String> consumer) {if (id instanceof ProjectComponentIdentifier) {consumer.accept(((ProjectComponentIdentifier) id).getProjectPath().intern(), "");} else if (id instanceof ModuleComponentIdentifier) {ModuleComponentIdentifier moduleComponentId = (ModuleComponentIdentifier) id;consumer.accept(moduleComponentId.getGroup() + ":" + moduleComponentId.getModule(),moduleComponentId.getVersion());} else if (id instanceof OpaqueComponentArtifactIdentifier) {// skip those for now.// These are file-based dependencies and it's unlikely to be an AAR.} else {getLogger().warn("Unknown ComponentIdentifier type: "+ id.getClass().getCanonicalName());}}

如果用compileOnly、provider修饰aar则会失败

验证下我们的想法,add 如下代码到app.build.gradle中

compileOnly 'com.facebook.stetho:stetho:1.5.0'

执行

./gradlew preDebugBuild

执行结果

可以反推compileOnly不支持修饰aar只支持jar
补充下AppClasspathCheckTask是AppPreBuildTask任务前置条件,在

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

  1. Android构建流程——篇一

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

  2. Android构建流程——篇七

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

  3. Android构建流程——篇六

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

  4. Android构建流程——篇五

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

  5. Android构建流程——篇四

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

  6. Android构建流程——篇三

    文章目录 Task5 checkDebugManifest 1. input/ouput 2. 核心类(CheckManifest) Task6 generateDebugBuildConfig 1. ...

  7. Android构建流程——篇八

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

  8. Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈)

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

  9. Google原生输入法LatinIME词库构建流程分析(二)

    在Google原生输入法LatinIME词库构建流程分析(一) 中分析LatinIME构建流程进行到了dict_trie->dict_list_->init_list这一步,然后就是构建N ...

最新文章

  1. 用 Python 爬了点你们喜欢的小电影
  2. Confluence 6 配置 HTTP 超时设置
  3. 转录组分析_高级转录组分析和R数据可视化
  4. python好用-Python里三个好用的调试神器
  5. [转]javascript中style.left和offsetLeft的使用
  6. gis里创建要素面板怎么打开_gis、mike学习
  7. 华硕无线路由打印机服务器,享受DIY的快乐 篇四:当普通打印机遇上智能路由器——网络打印机配置教程(以华硕AC66U B1为例)...
  8. linux 文件拆分 合并,Linux下文件的切分与合并的简单方法
  9. Skype协议分析[0]_安装Skype
  10. t分布 u分布 卡方分布_几种分布概述(正态分布/卡方分布/F分布/T分布)
  11. 字符型变量ch的值为英文字母 的c语言表达式
  12. C#脚本引擎CS-Script
  13. css float与学习骑自行车
  14. VMware之EXSI安装-yellowcong
  15. 少吃柿子、山楂、黑枣,警惕鞣酸
  16. 页面自动化之 selenium(一) 自动签到与签退
  17. Python使用ffmpy将amr格式的音频转化为mp3格式
  18. PoE视频监控解决方案
  19. Tame Me【驯服我】
  20. Zabbix4配置微信报警及消息群发

热门文章

  1. matlab函数表达式里分号_C语言表达式和语句
  2. centos7 nginx php整合,Centos7下,宿主机nginx配合docker环境的php-fpm
  3. keras从入门到放弃(二十二)一维卷积处理 RNN文本分类
  4. 微信小程序登录-利用Oenid实现白名单和黑名单
  5. 预训练模型真的越大越好吗?听听他们怎么说
  6. 薪资超大厂,校招天花板!Google大神云集,美团等参投,无人驾驶TOP独角兽!轻舟智航100+offer等你来!...
  7. 我宣布,这是我找到的史上AI最全论文体系!
  8. 2021蓝桥直播课-软件类-本科组
  9. Java软件研发工程师转行之深度学习(Deep Learning)进阶:手写数字识别+人脸识别+图像中物体分类+视频分类+图像与文字特征+猫狗分类
  10. hdu4280 Island Transport 网络流最大流 Dinic算法高效模板