Android中Gradle原理以及机制深入分析

Android中Gradle原理以及机制深入分析

一、Gradke构建流程

Gradle的构建主要分为三个阶段:

  • 1、初始化阶段(Initlization)

    读取setting.gradle,分析出项目包含的project(include的project)以及相关其他配置.

  • 2、配置阶段(Configuration)

    递归配置根项目和下面的子项目,主要涉及配置分析即task构建,最后生成task依赖有向图

  • 3、执行task

![](/Users/mikyou/Library/Application Support/marktext/images/2020-01-01-17-27-08-image.png)

二、APK打包流程

在介绍 Android Gradle Plugin Task 之前,我们先看看一个 apk 的构建流程,先放一张官方流程图:

android-app

APK打包流程如下:

  • 1、编译器将你的源码转化DEX(Dalvik Executable)文件(包括Android设备上运行的字节码),将所有其他内容转化成已经编译的资源。

  • 2、APK打包器将DEX文件(如果使用mulidex,则会生成多个DEX文件)和已经编译好的资源合并成单个APK。

  • 3、APK打包器使用debug或release密钥库对APK进行签名

  • 4、在生成最终APK之前,打包器会使用zipalign工具对应用进行优化,减少其在设备上的运行时占用的内存。

三、从Task维度打包APK

打包一个apk的命令:

./gradlew assembleDebug --console=plain

输出结果:

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

我们先看看每个 task 都是做什么的,以及其对应的实现类。
先回忆一下,我们在前面 android-gradle-plugin 主要流程分析里说到过,task 的实现可以在 TaskManager 里找到,创建 task 的方法主要是两个,TaskManager.createTasksBeforeEvaluate()ApplicationTaskManager.createTasksForVariantScope(),所以这些 task 的实现,也在这两个类里找就可以,下面列出了各个 task 的作用及实现类。

Task 对应实现类 作用
preBuild 空task,只做锚点使用
preDebugBuild 空task,只做锚点使用,与preBuild的区别是这个task是variant的锚点
compileAidl 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 CompatibleScreenManifests manifest 文件中生成 compatible-screens,指定屏幕适配
processDebugManifest MergeManifest 合并manifest文件
splitsDiscoveryTaskDebug SplitsDiscovery 生成split-list.json,用于apk分包
processDebugResources ProcessAndroidResources aapt打包资源
generateDebugSources 空task,锚点
javaPreCompileDebug 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,锚点

四、Task源码解析

在gradle plugin中Task主要有三种: 一种是普通的Task,一种是Incremental Task(增量Task),另一种就是transform

  • 解析普通的task源码

    1. 看 Task 继承的父类,一般来说,会继承 DefaultTask,IncrementalTask
    2. 看 @TaskAction 注解的方法,此方法就是这个 Task 做的事情
  • 如何读 IncrementalTask

我们先看看下这个类,这个类表示的是增量 Task,什么是增量呢?是相对于 全量来说的,全量我们可以理解为调用 clean 以后第一次编译的过程,这个就是全量编译,之后修改了代码或者资源文件,再次编译,就是增量编译。
其中比较重要的几个方法如下:

四、重点 Task 实现分析

上面每个 task 已经简单说明了具体做什么以及对应的实现类,下面选了几个比较重要的来分析一下其实现
为什么分析这几个呢?这几个代表了 gradle 自动生成代码,资源的处理,以及 dex 的处理,算是 apk 打包过程中比较重要的几环。
generateDebugBuildConfig
processDebugManifest
mergeDebugResources
processDebugResources
transformClassesWithDexBuilderForDebug
transformDexArchiveWithExternalLibsDexMergerForDebug
transformDexArchiveWithDexMergerForDebug

分析过程主要下面几个步骤,实现类,整体实现图,调用链路(方便以后回看代码),以及重要代码分析

4.1 generateDebugBuildConfig

4.1.1 实现类

GenerateBuildConfig

4.1.2 整体实现图

generateDebugBuildConfig

4.1.3 代码调用链路
GenerateBuildConfig.generate -> BuildConfigGenerator.generate -> JavaWriter
4.1.4 主要代码分析

在 GenerateBuildConfig 中,主要生成代码的步骤如下:

  1. 生成 BuildConfigGenerator
  2. 添加默认的属性,包括 DEBUG,APPLICATION_ID,FLAVOR,VERSION_CODE,VERSION_NAME
  3. 添加自定义属性
  4. 调用 JavaWriter 生成 BuildConfig.java 文件
// GenerateBuildConfig.generate()  
@TaskAction
void 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 mergeDebugResources

4.2.1 实现类

MergeResources

4.2.2 整体实现图

mergeDebugResources

4.2.3 调用链路
MergeResources.doFullTaskAction -> ResourceMerger.mergeData -> MergedResourceWriter.end -> QueueableAapt2.compile -> Aapt2QueuedResourceProcessor.compile -> AaptProcess.compile -> AaptV2CommandBuilder.makeCompile
4.2.4 主要代码分析

MergeResources 这个类,继承自 IncrementalTask,按照前面说的阅读增量 Task 代码的步骤,依次看三个方法的实现:isIncremental,doFullTaskAction,doIncrementalTaskAction

  • isIncremental
    // 说明 Task 支持增量
    protected boolean isIncremental() {
        return true;
    }
  • doFullTaskAction
  1. 通过 getConfiguredResourceSets() 获取 resourceSets,包括了自己的 res/ 和 依赖库的 res/ 以及 build/generated/res/rs
// MergeResources.doFullTaskAction()
List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor);
  1. 创建 ResourceMerger
// MergeResources.doFullTaskAction()
ResourceMerger merger = new ResourceMerger(minSdk);
  1. 创建 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)
  1. 将第一步获取的 resourceSet 加入 ResourceMerger 中
for (ResourceSet resourceSet : resourceSets) {
    resourceSet.loadFromFiles(getILogger());
    merger.addDataSet(resourceSet);
}
  1. 创建 MergedResourceWriter
  2. 调用 ResourceMerger.mergeData 合并资源
// MergeResources.doFullTaskAction()
merger.mergeData(writer, false /*doCleanUp*/);
  1. 调用 MergedResourceWriter 的 start(),addItem(),end() 方法,伪代码如下:
// DataMerger.mergeData
consumer.start()
for item in sourceSets:
  // item 包括了需要处理的资源,包括 xml 和 图片资源,每一个 item 对应的文件,会创建一个 CompileResourceRequest 对象,加入到 mCompileResourceRequests 里
  consumer.addItem(item)
consumer.end()
  1. 调用 QueueableAapt2 -> Aapt2QueuedResourceProcessor -> AaptProcess 处理资源
// MergedResourceWriter.end()
Future<File> result = this.mResourceCompiler.compile(new CompileResourceRequest(fileToCompile, request.getOutput(), request.getFolderName(), this.pseudoLocalesEnabled, this.crunchPng));
// AaptProcess.compile
public 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 processDebugResources

4.3.1 实现类

ProcessAndroidResources

4.3.2 整体实现图

processDebugResources

4.3.3 调用链路
ProcessAndroidResources.doFullTaskAction -> ProcessAndroidResources.invokeAaptForSplit -> AndroidBuilder.processResources -> QueueAapt2.link -> Aapt2QueuedResourceProcessor.link -> AaptProcess.link -> AaptV2CommandBuilder.makeLink
4.3.4 主要代码分析

ProcessAndroidResources 也是继承自 IncrementalTask,但是没有重写 isIncremental,所以不是增量的 Task,直接看 doFullTaskAction 即可

  • doFullTaskAction
    这个里面代码虽然多,但是主要的逻辑比较简单,就是调用 aapt2 link 去生成资源包。
    这里会处理 splits apk 相关的内容,关于 splits apk 具体可以查看 splits apk,简单来说,就是可以按照屏幕分辨率,abis 来生成不同的 apk,从而让特定用户的安装包变小。
    分下面几个步骤:
  1. 获取 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。

  1. 先处理 main 和 不依赖 density 的 ApkData 资源
// ProcessAndroidResources.doFullTaskAction
List<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;
        }
    }
}
  1. 调用 invokeAaptForSplit 处理资源
// ProcessAndroidResources.invokeAaptForSplit
void 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);
}
  1. 调用 AndroidBuilder.processResources -> QueueAapt2.link -> Aapt2QueuedResourceProcessor.link -> AaptProcess.link -> AaptV2CommandBuilder.makeLink 处理资源,生成资源包以及 R.java 文件
  2. 处理其他 ApkData 资源,这里只会生成资源包而不会生成 R.java 文件

关于 aapt2 的 compile 和 link 参数,可以在 developer.android.com/studio/comm… 这里看

4.4 processDebugManifest

4.4.1 实现类

MergeManifests

4.4.2 整体实现图

processDebugManifest

4.4.3 调用链路
MergeManifests.dofFullTaskAction -> AndroidBuilder.mergeManifestsForApplication -> Invoker.merge -> ManifestMerge2.merge
4.4.4 主要代码分析

MergeManifests 也是继承了 IncrementalTask,但是没有实现 isIncremental,所以只看其 doFullTaskAction 即可。
这个 task 功能主要是合并 mainfest,包括 module 和 flavor 里的,整个过程通过 MergingReport,ManifestMerger2 和 XmlDocument 进行。
这里直接看 ManifestMerger2.merge() 的 merge 过程 。
主要有几个步骤:

  1. 获取依赖库的 manifest 信息,用 LoadedManifestInfo 标识
  2. 获取主 module 的 manifest 信息
  3. 替换主 module 的 Manifest 中定义的某些属性,替换成 gradle 中定义的属性 例如: package, version_code, version_name, min_sdk_versin 等等
performSystemPropertiesInjection(mergingReportBuilder, xmlDocumentOptional.get());
// ManifestMerger2.performSystemPropertiesInjection
protected 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);
        }
    }
}
  1. 合并 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();
    }
}
  1. 合并依赖库的 manifest
for (LoadedManifestInfo libraryDocument : loadedLibraryDocuments) {
    mLogger.verbose("Merging library manifest " + libraryDocument.getLocation());
    xmlDocumentOptional = merge(
            xmlDocumentOptional, libraryDocument, mergingReportBuilder);
    if (!xmlDocumentOptional.isPresent()) {
        return mergingReportBuilder.build();
    }
}
  1. 处理 manifest 的 placeholders
performPlaceHolderSubstitution(loadedMainManifestInfo, xmlDocumentOptional.get(), mergingReportBuilder, severity);
  1. 之后对最终合并后的 manifest 中的一些属性重新进行一次替换,类似步骤 4
  2. 保存 manifest 到 build/intermediates/manifest/fullxxx/AndroidManifest.xml
    这就生成了最终的 Manifest 文件

4.5 transformClassesWithDexBuilderForDebug

4.5.1 实现类

DexArchiveBuilderTransform

4.5.2 整体实现图

transformClassesWithDexBuilderForDebug

4.5.3 调用链路
DexArchiveBuilderTransform.transform -> DexArchiveBuilderTransform.convertJarToDexArchive -> DexArchiveBuilderTransform.convertToDexArchive -> DexArchiveBuilderTransform.launchProcessing -> DxDexArchiveBuilder.convert
4.5.4 主要代码分析

在 DexArchiveBuilderTransform 中,对 class 的处理分为两种方式,一种是对 目录下的 class 进行处理,一种是对 .jar 里的 class 进行处理。
为什么要分为这两种方式呢?.jar 中的 class 一般来说都是依赖库,基本上不会改变,gradle 在这里做了一个缓存,但是两种方式最终都会调用到 convertToDexArchive,可以说是殊途同归吧。

  • convertJarToDexArchive 处理 jar
    处理 .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 中,有下面几个步骤:

  1. 判断目录下的 class 是否新增或者修改过
  2. 调用 DexArchiveBuilder.build 去处理修改过的 class
  3. DexArchiveBuilder 有两个子类,D8DexArchiveBuilder 和 DxDexArchiveBuilder,分别是调用 d8 和 dx 去打 dex

4.6 transformDexArchiveWithExternalLibsDexMergerForDebug

4.6.1 实现类

ExternalLibsMergerTransform

4.6.2 整体实现图

transformDexArchiveWithExternalLibsDexMergerForDebug

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 transformDexArchiveWithDexMergerForDebug

4.7.1 实现类

DexMergerTransform

4.7.2 整体实现图

transformDexArchiveWithDexMergerForDebug

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 命令

五、本文重点

  1. Android Gradle Plugin 中各个 Task 的作用及实现类,具体可参考文中第二节「Task 对应实现类」
  2. 如何阅读 Task 的代码

   转载规则


《Android中Gradle原理以及机制深入分析》 mikyou 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Java中的Vector源码解析 Java中的Vector源码解析
Java中的Vector源码解析一、Vector介绍Vector 是一个矢量队列,它的继承关系和ArrayList是一样的,同样实现了RandomAccess标记接口,用来表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般
2020-01-02
下一篇 
Android中Gradle常用的配置 Android中Gradle常用的配置
Android中Gradle常用的配置1、Gradle是什么? Gradle是一个自动化构建工具 兼容Maven等仓库 基于Groovy的特定领域语言来声明名目设置 2、GradleWraper Gradle Wrapper是一个脚
2020-01-01