【Android 修炼手册】Gradle 篇 -- Android Gradle Plugin 主要 Task 分析
预备知识理解 gradle 的基本开发了解 gradle task 和 plugin 使用及开发了解 androID gradle plugin 的使用看完本文可以达到什么程度了解 androID gradle plugin 中各个 task 作用了解 androID gradle plugin 中主要 task 的实现阅读前准备工作1.项目添加 androID gradle plugin 依赖
compile 'com.androID.tools.build:gradle:3.0.1'
通过这种方式,可以直接依赖 plugin 的源码,读起来比较方便
2.官方对照源码地址 android gradle plugin 源码地址
大家可以直接 clone EasyGradle 项目,把 app/build.gradle 里的 implementation 'com.androID.tools.build:gradle:3.0.1' 注释打开就可以了。
在 Gradle的基本使用 和 Android Gradle Plugin 主要流程分析 里,我们知道了 gradle 中 task 的重要性,以及 androID gradle plugin 的主要流程,这一篇就来分析一下 androID gradle plugin 中一些重要的 task 是怎么执行的。
一、AndroID 打包流程在介绍 AndroID Gradle Plugin Task 之前,我们先看看一个 apk 的构建流程,先放一张官方流程图:
官方介绍的流程如下:
编译器将您的源代码转换成 DEX(Dalvik Executable) 文件(其中包括 AndroID 设备上运行的字节码),将所有其他内容转换成已编译资源。APK 打包器将 DEX 文件和已编译资源合并成单个 APK。 不过,必须先签署 APK,才能将应用安装并部署到 AndroID 设备上。APK 打包器使用调试或发布密钥库签署您的 APK:在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,减少其在设备上运行时占用的内存。那么以 Task 的维度来看 apk 的打包,是什么流程呢?我们先执行下面的命令,看一下打包一个 apk 需要哪些 task
首先我们看一下 打包一个 apk 需要哪些 task。 在项目根目录下执行命令
./gradlew android-gradle-plugin-source:assembleDeBUG --console=plain
看一下输出结果
:android-gradle-plugin-source:preBuild UP-TO-DATE:android-gradle-plugin-source:preDeBUGBuild:android-gradle-plugin-source:compileDeBUGAIDl:android-gradle-plugin-source:compileDeBUGRenderscript:android-gradle-plugin-source:checkDeBUGManifest:android-gradle-plugin-source:generateDeBUGBuildConfig:android-gradle-plugin-source:preparelintJar UP-TO-DATE:android-gradle-plugin-source:generateDeBUGResValues:android-gradle-plugin-source:generateDeBUGResources:android-gradle-plugin-source:mergeDeBUGResources:android-gradle-plugin-source:createDeBUGCompatibleScreenManifests:android-gradle-plugin-source:processDeBUGManifest:android-gradle-plugin-source:splitsdiscoveryTaskDeBUG:android-gradle-plugin-source:processDeBUGResources:android-gradle-plugin-source:generateDeBUGSources:android-gradle-plugin-source:java@R_207_3013@eBUG:android-gradle-plugin-source:compileDeBUGJavaWithJavac:android-gradle-plugin-source:compileDeBUGNdk NO-SOURCE:android-gradle-plugin-source:compileDeBUGSources:android-gradle-plugin-source:mergeDeBUGShaders:android-gradle-plugin-source:compileDeBUGShaders:android-gradle-plugin-source:generateDeBUGAssets:android-gradle-plugin-source:mergeDeBUGAssets:android-gradle-plugin-source:transformClassesWithDexBuilderForDeBUG:android-gradle-plugin-source:transformDexArchiveWithExternallibsDexMergerForDeBUG:android-gradle-plugin-source:transformDexArchiveWithDexMergerForDeBUG:android-gradle-plugin-source:mergeDeBUGJnilibFolders:android-gradle-plugin-source:transformNativelibsWithMergeJnilibsForDeBUG:android-gradle-plugin-source:transformNativelibsWithStripDeBUGSymbolForDeBUG:android-gradle-plugin-source:processDeBUGJavaRes NO-SOURCE:android-gradle-plugin-source:transformResourcesWithMergeJavaResForDeBUG:android-gradle-plugin-source:valIDateSigningDeBUG:android-gradle-plugin-source:packageDeBUG:android-gradle-plugin-source:assembleDeBUG
上面就是打包一个 apk 需要的 task
二、Task 对应实现类我们先看看每个 task 都是做什么的,以及其对应的实现类。
先回忆一下,我们在前面 android-gradle-plugin 主要流程分析里说到过,task 的实现可以在 TaskManager 里找到,创建 task 的方法主要是两个,TaskManager.createTasksBeforeEvaluate() 和 ApplicationTaskManager.createTasksForVariantScope(),所以这些 task 的实现,也在这两个类里找就可以,下面列出了各个 task 的作用及实现类。
Task | 对应实现类 | 作用 |
---|---|---|
preBuild | 空 task,只做锚点使用 | |
preDeBUGBuild | 空 task,只做锚点使用,与 preBuild 区别是这个 task 是 variant 的锚点 | |
compileDeBUGAIDl | AIDlCompile | 处理 aIDl |
compileDeBUGRenderscript | RenderscriptCompile | 处理 renderscript |
checkDeBUGManifest | CheckManifest | 检测 manifest 是否存在 |
generateDeBUGBuildConfig | GenerateBuildConfig | 生成 BuildConfig.java |
preparelintJar | PreparelintJar | 拷贝 lint jar 包到指定位置 |
generateDeBUGResValues | GenerateResValues | 生成 resvalues,generated.xml |
generateDeBUGResources | 空 task,锚点 | |
mergeDeBUGResources | MergeResources | 合并资源文件 |
createDeBUGCompatibleScreenManifests | CompatibleScreensManifest | manifest 文件中生成 compatible-screens,指定屏幕适配 |
processDeBUGManifest | MergeManifests | 合并 manifest 文件 |
splitsdiscoveryTaskDeBUG | Splitsdiscovery | 生成 split-List.Json,用于 apk 分包 |
processDeBUGResources | ProcessAndroIDResources | aapt 打包资源 |
generateDeBUGSources | 空 task,锚点 | |
java@R_207_3013@eBUG | JavaPreCompileTask | 生成 annotationProcessors.Json 文件 |
compileDeBUGJavaWithJavac | AndroIDJavaCompile | 编译 java 文件 |
compileDeBUGNdk | NdkCompile | 编译 ndk |
compileDeBUGSources | 空 task,锚点使用 | |
mergeDeBUGShaders | MergeSourceSetFolders | 合并 shader 文件 |
compileDeBUGShaders | shadercompile | 编译 shaders |
generateDeBUGAssets | 空 task,锚点 | |
mergeDeBUGAssets | MergeSourceSetFolders | 合并 assets 文件 |
transformClassesWithDexBuilderForDeBUG | DexArchiveBuildertransform | class 打包 dex |
transformDexArchiveWithExternallibsDexMergerForDeBUG | ExternallibsMergertransform | 打包三方库的 dex,在 dex 增量的时候就不需要再 merge 了,节省时间 |
transformDexArchiveWithDexMergerForDeBUG | DexMergertransform | 打包最终的 dex |
mergeDeBUGJnilibFolders | MergeSouceSetFolders | 合并 jni lib 文件 |
transformNativelibsWithMergeJnilibsForDeBUG | MergeJavaResourcestransform | 合并 jnilibs |
transformNativelibsWithStripDeBUGSymbolForDeBUG | StripDeBUGSymboltransform | 去掉 native lib 里的 deBUG 符号 |
processDeBUGJavaRes | ProcessJavaResConfigAction | 处理 java res |
transformResourcesWithMergeJavaResForDeBUG | MergeJavaResourcestransform | 合并 java res |
valIDateSigningDeBUG | ValIDateSigningTask | 验证签名 |
packageDeBUG | PackageApplication | 打包 apk |
assembleDeBUG | 空 task,锚点 |
在 gradle plugin 中的 Task 主要有三种,一种是普通的 task,一种是增量 task,一种是 transform,下面分别看下这三种 task 怎么去读。
如何读 Task 的代码看 Task 继承的父类,一般来说,会继承 DefaultTask,IncrementalTask看 @TaskAction 注解的方法,此方法就是这个 Task 做的事情如何读 IncrementalTask我们先看看下这个类,这个类表示的是增量 Task,什么是增量呢?是相对于 全量来说的,全量我们可以理解为调用 clean 以后第一次编译的过程,这个就是全量编译,之后修改了代码或者资源文件,再次编译,就是增量编译。
其中比较重要的几个方法如下:
public abstract class IncrementalTask extends BaseTask { // ... @Internal protected boolean isIncremental() { // 是否需要增量,默认是 false return false; } // 需要子类实现,全量的时候执行的任务 protected abstract voID doFullTaskAction() throws Exception; // 增量的时候执行的任务,默认是什么都不执行,参数是增量的时候修改过的文件 protected voID doIncrementalTaskAction(Map<file, fileStatus> changedinputs) throws Exception { } @TaskAction voID taskAction(IncrementalTaskinputs inputs) throws Exception { // 判断是否是增量 if(this.isIncremental() && inputs.isIncremental()) { this.doIncrementalTaskAction(this.getChangedinputs(inputs)); } else { this.getProject().getLogger().info("Unable do incremental execution: full task run"); this.doFullTaskAction(); } } // 获取修改的文件 private Map<file, fileStatus> getChangedinputs(IncrementalTaskinputs inputs) { Map<file, fileStatus> changedinputs = Maps.newHashMap(); inputs.outOfDate((change) -> { fileStatus status = change.isAdded()?fileStatus.NEW:fileStatus.CHANGED; changedinputs.put(change.getfile(), status); }); inputs.removed((change) -> { fileStatus var10000 = (fileStatus)changedinputs.put(change.getfile(), fileStatus.REMOVED); }); return changedinputs; }}
简单介绍了 IncrementalTask 之后,我们这里强调一下,如何去读一个 增量 Task 的代码,主要有四步:
首先这个 Task 要继承 IncrementalTask,其次看 isIncremental 方法,如果返回 true,说明支持增量,返回 false 则不支持然后看 doFullTaskAction 方法,是全量的时候执行的 *** 作最后看 doIncrementalTaskAction 方法,这里是增量的时候执行的 *** 作如何读 transform继承自 transform看其 transform 方法的实现四、重点 Task 实现分析上面每个 task 已经简单说明了具体做什么以及对应的实现类,下面选了几个比较重要的来分析一下其实现
为什么分析这几个呢?这几个代表了 gradle 自动生成代码,资源的处理,以及 dex 的处理,算是 apk 打包过程中比较重要的几环。
generateDeBUGBuildConfig
processDeBUGManifest
mergeDeBUGResources
processDeBUGResources
transformClassesWithDexBuilderForDeBUG
transformDexArchiveWithExternallibsDexMergerForDeBUG
transformDexArchiveWithDexMergerForDeBUG
分析过程主要下面几个步骤,实现类,整体实现图,调用链路(方便以后回看代码),以及重要代码分析
4.1 generateDeBUGBuildConfig4.1.1 实现类GenerateBuildConfig
4.1.2 整体实现图
4.1.3 代码调用链路
GenerateBuildConfig.generate -> BuildConfigGenerator.generate -> JavaWriter4.1.4 主要代码分析
在 GenerateBuildConfig 中,主要生成代码的步骤如下:
生成 BuildConfigGenerator添加默认的属性,包括 DEBUG,APPliCATION_ID,FLAVOR,VERSION_CODE,VERSION_name添加自定义属性调用 JavaWriter 生成 BuildConfig.java 文件// GenerateBuildConfig.generate() @TaskActionvoID generate() throws IOException { // ... BuildConfigGenerator generator = new BuildConfigGenerator( getSourceOutputDir(), getBuildConfigPackagename()); // 添加默认的属性,包括 DEBUG,APPliCATION_ID,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()); // 添加自定义属性 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) + '"'); } } // 内部调用 JavaWriter 生成 java 文件 generator.generate();}4.2 mergeDeBUGResources4.2.1 实现类
MergeResources
4.2.2 整体实现图
4.2.3 调用链路
MergeResources.doFullTaskAction -> ResourceMerger.mergeData -> MergedResourceWriter.end -> QueueableAapt2.compile -> Aapt2QueuedResourceProcessor.compile -> AaptProcess.compile -> AaptV2CommandBuilder.makeCompile4.2.4 主要代码分析
MergeResources 这个类,继承自 IncrementalTask,按照前面说的阅读增量 Task 代码的步骤,依次看三个方法的实现:isIncremental,doFullTaskAction,doIncrementalTaskAction
isIncremental// 说明 Task 支持增量 protected boolean isIncremental() { return true; }doFullTaskAction通过 getConfiguredResourceSets() 获取 resourceSets,包括了自己的 res/ 和 依赖库的 res/ 以及 build/generated/res/rs
// MergeResources.doFullTaskAction()List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor);创建 ResourceMerger
// MergeResources.doFullTaskAction()ResourceMerger merger = new ResourceMerger(minSdk);创建 QueueableResourceCompiler,因为 gradle3.x 以后支持了 aapt2,所以这里有两种选择 aapt 和 aapt2。其中 aapt2 有三种模式,OutOfProcessAaptV2,AaptV2Jni,QueueableAapt2,这里默认创建了 QueueableAapt2,resourceCompiler = QueueableAapt2
// MergeResources.doFullTaskAction()// makeAapt 中会判断使用 aapt 还是 aapt2,这里以 aapt2 为例,返回的是 QueueableAapt2 对象QueueableResourceCompiler resourceCompiler = makeAapt( aaptGeneration, getBuilder(), fileCache, crunchPng, variantScope, getAaptTempDir(), mergingLog)将第一步获取的 resourceSet 加入 ResourceMerger 中
for (ResourceSet resourceSet : resourceSets) { resourceSet.loadFromfiles(getILogger()); merger.addDataSet(resourceSet);}创建 MergedResourceWriter调用 ResourceMerger.mergeData 合并资源
// MergeResources.doFullTaskAction()merger.mergeData(writer, false /*doCleanUP*/);调用 MergedResourceWriter 的 start(),addItem(),end() 方法,伪代码如下:
// DataMerger.mergeDataconsumer.start()for item in sourceSets: // item 包括了需要处理的资源,包括 xml 和 图片资源,每一个 item 对应的文件,会创建一个 CompileResourceRequest 对象,加入到 mCompileResourceRequests 里 consumer.addItem(item)consumer.end()调用 QueueableAapt2 -> Aapt2QueuedResourceProcessor -> AaptProcess 处理资源
// MergedResourceWriter.end()Future<file> result = this.mResourceCompiler.compile(new CompileResourceRequest(fileToCompile, request.getoutput(), request.getFoldername(), this.pseudolocalesEnabled, this.crunchPng));// AaptProcess.compilepublic voID compile( @NonNull CompileResourceRequest request, @NonNull Job<AaptProcess> job, @Nullable ProcessOutputHandler processOutputHandler) throws IOException { // ... // 使用 AaptV2CommandBuilder 生成 aapt2 命令 mWriter.write(joiner.join(AaptV2CommandBuilder.makeCompile(request))); mWriter.flush(); // 输出命令}
这一步调用 aapt2 命令去处理资源,处理完以后 xxx.xml.flat 格式
doIncrementalTaskAction增量任务过程和全量其实差异不大,只不过是在获取 resourceSets 的时候,使用的是修改后的文件4.3 processDeBUGResources4.3.1 实现类
ProcessAndroIDResources
4.3.2 整体实现图4.3.3 调用链路ProcessAndroIDResources.doFullTaskAction -> ProcessAndroIDResources.invokeAaptForSplit -> AndroIDBuilder.processResources -> QueueAapt2.link -> Aapt2QueuedResourceProcessor.link -> AaptProcess.link -> AaptV2CommandBuilder.makelink4.3.4 主要代码分析
ProcessAndroIDResources 也是继承自 IncrementalTask,但是没有重写 isIncremental,所以不是增量的 Task,直接看 doFullTaskAction 即可
doFullTaskAction这个里面代码虽然多,但是主要的逻辑比较简单,就是调用 aapt2 link 去生成资源包。
这里会处理 splits apk 相关的内容,关于 splits apk 具体可以查看 splits apk,简单来说,就是可以按照屏幕分辨率,abis 来生成不同的 apk,从而让特定用户的安装包变小。
分下面几个步骤:获取 split 数据
List<ApkData> splitsToGenerate = getApksToGenerate(outputScope, supportedAbis, buildTargetAbi, buildTargetDensity);
返回的是一个 ApkData 列表,ApkData 有三个子类,分别是 Main,Universal,FullSplit
我们配置 如下:
androID { splits { // Configures multiple APKs based on screen density. density { // Configures multiple APKs based on screen density. enable true // SpecifIEs a List of screen densitIEs Gradle should not create multiple APKs for. exclude "ldpi", "xxhdpi", "xxxhdpi" // SpecifIEs a List of compatible screen size settings for the manifest. compatibleScreens 'small', 'normal', 'large', 'xlarge' } }}
这里的 ApkData 会返回一个 Universal 和多个 FullSplit,Universal 代表的是主 apk,FullSplit 就是根据屏幕密度拆分的 apk。
如果我们没有配置 splits apk,那么这里只会返回一个 Main 的实例,标识完整的 apk。
2. 先处理 main 和 不依赖 density 的 ApkData 资源
// ProcessAndroIDResources.doFullTaskActionList<ApkData> apkDataList = new ArrayList<>(splitsToGenerate);for (ApkData apkData : splitsToGenerate) { if (apkData.requiresAapt()) { // 这里只处理 main 和不依赖 density 的资源 boolean codeGen = (apkData.getType() == Outputfile.OutputType.MAIN || apkData.getFilter(Outputfile.FilterType.DENSITY) == null); if (codeGen) { apkDataList.remove(apkData); invokeAaptForSplit( manifestsOutputs, libraryInfoList, packageIDfileSet, splitList, featureResourcePackages, apkData, codeGen, aapt); break; } }}调用 invokeAaptForSplit 处理资源
// ProcessAndroIDResources.invokeAaptForSplitvoID invokeAaptForSplit(...) { // ... String packageForR = null; file srcOut = null; file symbolOutputDir = null; file proguardOutputfile = null; file mainDexListProguardOutputfile = null; // 如果传了 generateCode 参数,会生成 R.java if (generateCode) { packageForR = originalApplicationID; // we have to clean the source folder output in case the package name changed. srcOut = getSourceOutputDir(); if (srcOut != null) { fileUtils.cleanOutputDir(srcOut); } symbolOutputDir = textSymbolOutputDir.get(); proguardOutputfile = getProguardOutputfile(); mainDexListProguardOutputfile = getMainDexListProguardOutputfile(); } // ... getBuilder().processResources(aapt, config);}调用 AndroIDBuilder.processResources -> QueueAapt2.link -> Aapt2QueuedResourceProcessor.link -> AaptProcess.link -> AaptV2CommandBuilder.makelink 处理资源,生成资源包以及 R.java 文件处理其他 ApkData 资源,这里只会生成资源包而不会生成 R.java 文件
关于 aapt2 的 compile 和 link 参数,可以在 developer.android.com/studio/comm… 这里看
4.4 processDeBUGManifest4.4.1 实现类MergeManifests
4.4.2 整体实现图
4.4.3 调用链路
MergeManifests.dofFullTaskAction -> AndroIDBuilder.mergeManifestsForApplication -> Invoker.merge -> ManifestMerge2.merge4.4.4 主要代码分析
MergeManifests 也是继承了 IncrementalTask,但是没有实现 isIncremental,所以只看其 doFullTaskAction 即可。
这个 task 功能主要是合并 mainfest,包括 module 和 flavor 里的,整个过程通过 MergingReport,ManifestMerger2 和 Xmldocument 进行。
这里直接看 ManifestMerger2.merge() 的 merge 过程 。主要有几个步骤:
performSystemPropertIEsInjection(mergingReportBuilder, xmldocumentOptional.get());// ManifestMerger2.performSystemPropertIEsInjectionprotected voID performSystemPropertIEsInjection( @NonNull MergingReport.Builder mergingReport, @NonNull Xmldocument xmldocument) { for (ManifestSystemProperty manifestSystemProperty : ManifestSystemProperty.values()) { String propertyOverrIDe = mSystemPropertyResolver.getValue(manifestSystemProperty); if (propertyOverrIDe != null) { manifestSystemProperty.addTo( mergingReport.getActionRecorder(), xmldocument, propertyOverrIDe); } }}
4.合并 flavor,buildType 中的 manifest
for (file inputfile : mFlavorsAndBuildTypefiles) { LoadedManifestInfo overlaydocument = load( new ManifestInfo(null, inputfile, Xmldocument.Type.OVERLAY, Optional.of(mainPackageAttribute.get().getValue())), selectors, mergingReportBuilder); // 检查 package 定义 Optional<XmlAttribute> packageAttribute = overlaydocument.getXmldocument().getPackage(); if (loadedMainManifestInfo.getoriginalPackagename().isPresent() && packageAttribute.isPresent() && !loadedMainManifestInfo.getoriginalPackagename().get().equals( packageAttribute.get().getValue())) { // 如果 package 定义重复的话,会输出下面信息,我们平时应该或多或少见过类似的错误 String message = mMergeType == MergeType.APPliCATION ? String.format( "Overlay manifest:package attribute declared at %1$s value=(%2$s)\n" + "\thas a different value=(%3$s) " + "declared in main manifest at %4$s\n" + "\tSuggestion: remove the overlay declaration at %5$s " + "\tand place it in the build.gradle:\n" + "\t\tflavorname {\n" + "\t\t\tapplicationID = \"%2$s\"\n" + "\t\t}", packageAttribute.get().printposition(), packageAttribute.get().getValue(), mainPackageAttribute.get().getValue(), mainPackageAttribute.get().printposition(), packageAttribute.get().getSourcefile().print(true)) : String.format( "Overlay manifest:package attribute declared at %1$s value=(%2$s)\n" + "\thas a different value=(%3$s) " + "declared in main manifest at %4$s", packageAttribute.get().printposition(), packageAttribute.get().getValue(), mainPackageAttribute.get().getValue(), mainPackageAttribute.get().printposition()); // ... return mergingReportBuilder.build(); }}合并依赖库的 manifest
for (LoadedManifestInfo librarydocument : loadedlibrarydocuments) { mLogger.verbose("Merging library manifest " + librarydocument.getLocation()); xmldocumentOptional = merge( xmldocumentOptional, librarydocument, mergingReportBuilder); if (!xmldocumentOptional.isPresent()) { return mergingReportBuilder.build(); }}处理 manifest 的 placeholders
performPlaceHolderSubstitution(loadedMainManifestInfo, xmldocumentOptional.get(), mergingReportBuilder, severity);之后对最终合并后的 manifest 中的一些属性重新进行一次替换,类似步骤 4保存 manifest 到 build/intermediates/manifest/fullxxx/AndroIDManifest.xml 这就生成了最终的 Manifest 文件4.5 transformClassesWithDexBuilderForDeBUG4.5.1 实现类
DexArchiveBuildertransform
4.5.2 整体实现图
4.5.3 调用链路
DexArchiveBuildertransform.transform -> DexArchiveBuildertransform.convertJarToDexArchive -> DexArchiveBuildertransform.convertToDexArchive -> DexArchiveBuildertransform.launchProcessing -> DxDexArchiveBuilder.convert4.5.4 主要代码分析
在 DexArchiveBuildertransform 中,对 class 的处理分为两种方式,一种是对 目录下的 class 进行处理,一种是对 .jar 里的 class 进行处理。
为什么要分为这两种方式呢?.jar 中的 class 一般来说都是依赖库,基本上不会改变,gradle 在这里做了一个缓存,但是两种方式最终都会调用到 convertToDexArchive,可以说是殊途同归吧。
处理 .jar 时,会对 jar 包中的每一个 class 都单独打成一个 .dex 文件,之后还是放在 .jar 包中
private List<file> convertJarToDexArchive( @NonNull Context context, @NonNull Jarinput toConvert, @NonNull transformOutputProvIDer transformOutputProvIDer) throws Exception { file cachedVersion = cacheHandler.getCachedVersionIfPresent(toConvert); if (cachedVersion == null) { // 如果没有缓存,调用 convertToDexArchive 去生成 dex return convertToDexArchive(context, toConvert, transformOutputProvIDer, false); } else { // 如果有缓存,直接使用缓存的 jar file outputfile = getPreDexJar(transformOutputProvIDer, toConvert, null); files.copy( cachedVersion.topath(), outputfile.topath(), StandardcopyOption.REPLACE_EXISTING); // no need to try to cache an already cached version. return ImmutableList.of(); } }convertToDexArchive 处理 dir 以及 jar 的后续处理
对 dir 处理使用 convertToDexArchive
其中会调用 launchProcessing
private static voID launchProcessing( @NonNull DexConversionParameters dexConversionParameters, @NonNull OutputStream outStream, @NonNull OutputStream errStream) throws IOException, URISyntaxException { // ... boolean hasIncrementalinfo = dexConversionParameters.isDirectoryBased() && dexConversionParameters.isIncremental; // 判断 class 是否新增或者修改过,如果新增或者修改过,就需要处理 Predicate<String> toprocess = hasIncrementalinfo ? path -> { Map<file, Status> changedfiles = ((Directoryinput) dexConversionParameters.input) .getChangedfiles(); file resolved = inputPath.resolve(path).tofile(); Status status = changedfiles.get(resolved); return status == Status.ADDED || status == Status.CHANGED; } : path -> true; bucketFilter = bucketFilter.and(toprocess); try (Classfileinput input = Classfileinputs.fromPath(inputPath)) { // 内部调用 dx 或者 d8 去打 dex dexArchiveBuilder.convert( input.entrIEs(bucketFilter), Paths.get(new URI(dexConversionParameters.output)), dexConversionParameters.isDirectoryBased()); } catch (DexArchiveBuilderException ex) { throw new DexArchiveBuilderException("Failed to process " + inputPath.toString(), ex); } }
在 launchProcessing 中,有下面几个步骤:
判断目录下的 class 是否新增或者修改过调用 DexArchiveBuilder.build 去处理修改过的 classDexArchiveBuilder 有两个子类,D8DexArchiveBuilder 和 DxDexArchiveBuilder,分别是调用 d8 和 dx 去打 dex4.6 transformDexArchiveWithExternallibsDexMergerForDeBUG4.6.1 实现类ExternallibsMergertransform
4.6.2 整体实现图4.6.3 调用链路
这一步是处理依赖库的 dex,把上一步生成的依赖库的 dex merge 成一个 dex
// dx ExternallibsMergertransform.transform -> DexMergertransformCallable.call -> DxDexArchiveMerger.mergeDexArchives -> DxDexArchiveMerger.mergeMonoDex -> DexArchiveMergerCallable.call -> DexMerger.merge
// d8ExternallibsMergertransform.transform -> DexMergertransformCallable.call -> D8DexArchiveMerger.mergeDexArchives -> 调用 D8 命令
这里逻辑比较简单,就不具体分析了
4.7 transformDexArchiveWithDexMergerForDeBUG4.7.1 实现类DexMergertransform
4.7.2 整体实现图
4.7.3 调用链路
和上一步类似
// dx DexMergertransform.transform -> DexMergertransform.handleLegacyAndMonoDex -> DexMergertransformCallable.call -> DxDexArchiveMerger.mergeDexArchives -> DxDexArchiveMerger.mergeMonoDex -> DexArchiveMergerCallable.call -> DexMerger.merge
// d8DexMergertransform.transform -> DexMergertransform.handleLegacyAndMonoDex -> DexMergertransformCallable.call -> D8DexArchiveMerger.mergeDexArchives -> 调用 D8 命令五、本文重点AndroID Gradle Plugin 中各个 Task 的作用及实现类,具体可参考文中第二节「Task 对应实现类」如何阅读 Task 的代码
总结
以上是内存溢出为你收集整理的Gradle之Android Gradle Plugin 主要 Task 分析(三)全部内容,希望文章能够帮你解决Gradle之Android Gradle Plugin 主要 Task 分析(三)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)