【灵魂七问】深度探索 Gradle 自动化构建技术(5、Gradle 插件架构实现原理剖析 — 下)

前言

成为一名优秀的Android开发,须要一份完备的知识体系,在这里,让咱们一块儿成长为本身所想的那样~。

5、AppPlugin 构建流程

为了可以查看 Android Gradle Plugin 与 Gradle 的源码,咱们须要在项目中添加 android gradle plugin 依赖,以下所示:html

compile 'com.android.tools.build:gradle:3.0.1'
复制代码

众所周知,咱们可以将一个 moudle 构建为一个 Android 项目是因为在 build.gradle 中配置了以下的插件应用代码:java

apply plugin: 'com.android.application'
复制代码

当执行到 apply plugin: 'com.android.application' 这行配置时,也就开始了 AppPlugin 的构建流程,下面咱们就来分析下 AppPlugin 的构建流程。android

'com.android.application' 对应的插件 properties 为 'com.android.internal.application',内部标明的插件实现类以下所示:git

implementation-class=com.android.build.gradle.internal.plugins.AppPlugin
复制代码

AppPlugin 中的关键实现代码以下所示:github

/** Gradle plugin class for 'application' projects, applied on the base application module */
public class AppPlugin extends AbstractAppPlugin {
    ...

    // 应用指定的 plugin,这里是一个空实现
    @Override
    protected void pluginSpecificApply(@NonNull Project project) {
    }

    ...

    // 获取一个扩展类:应用 application plugin 都会提供一个与之对应的 android extension
    @Override
    @NonNull
    protected Class<? extends AppExtension> getExtensionClass() {
        return BaseAppModuleExtension.class;
    }

    ...
}
复制代码

那 apply 方法是在什么地方被调用的呢?web

首先,咱们梳理下 AppPlugin 的继承关系,以下所示:json

AppPlugin => AbstractAppPlugin => BasePlugin
复制代码

而 apply 方法就在 BasePlugin 类中,BasePlugin 是一个应用于全部 Android Plugin 的基类,在 apply 方法中会预先进行一些准备工做缓存

一、准备工做

当编译器执行到 apply plugin 这行 groovy 代码时,gradle 便会最终回调 BasePlugin 基类 的 apply 方法,以下所示:微信

@Override
public final void apply(@NonNull Project project) {
    CrashReporting.runAction(
            () -> {
                basePluginApply(project);
                pluginSpecificApply(project);
            });
}
复制代码

在 apply 方法中调用了 basePluginApply 方法,其源码以下所示:闭包

private void basePluginApply(@NonNull Project project) {
       
        ...
        
        // 一、DependencyResolutionChecks 会检查并确保在配置阶段不去解析依赖。
        DependencyResolutionChecks.registerDependencyCheck(project, projectOptions);

        // 二、应用一个 AndroidBasePlugin,目的是为了让其余插件做者区分当前应用的是一个 Android 插件。
        project.getPluginManager().apply(AndroidBasePlugin.class);

        // 三、检查 project 路径是否有错误,发生错误则抛出 StopExecutionException 异常。
        checkPathForErrors();
        
        // 四、检查子 moudle 的结构:目前版本会检查 2 个模块有没有相同的标识(组+名称),若是有则抛出 StopExecutionException 异常。(组件化在不一样的 moudle 中须要给资源加 prefix 前缀)
        checkModulesForErrors();

        // 五、插件初始化,必须当即执行。此外,须要注意,Gradle Deamon 永远不会同时执行两个构建流程。
        PluginInitializer.initialize(project);
        
        // 六、初始化用于记录构建过程当中配置信息的工厂实例 ProcessProfileWriterFactory
        RecordingBuildListener buildListener = ProfilerInitializer.init(project, projectOptions);
        
        // 七、给 project 设置 android plugin version、插件类型、插件生成器、project  选项
        ProcessProfileWriter.getProject(project.getPath())
                .setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION)
                .setAndroidPlugin(getAnalyticsPluginType())
                .setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST)
                .setOptions(AnalyticsUtil.toProto(projectOptions));

        // 配置工程
        threadRecorder.record(
                ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
                project.getPath(),
                null,
                this::configureProject);

        // 配置 Extension
        threadRecorder.record(
                ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
                project.getPath(),
                null,
                this::configureExtension);

        // 建立 Tasks
        threadRecorder.record(
                ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
                project.getPath(),
                null,
                this::createTasks);
    }
复制代码

能够看到,前 4 个步骤都是一些检查操做,然后 3 个步骤则是对插件进行初始化与配置。咱们 梳理下 准备工程 中的任务,以下所示:

  • 一、 插件检查操做
    • 1)、 使用 DependencyResolutionChecks 类去检查并确保在配置阶段不去解析依赖
    • 2)、 应用一个 AndroidBasePlugin,目的是为了让其余插件做者区分当前应用的是一个 Android 插件
    • 3)、 检查 project 路径是否有错误,发生错误则抛出 StopExecutionException 异常
    • 4)、 检查子 moudle 的结构,目前版本会检查 2 个模块有没有相同的标识(组 + 名称),若是有则抛出 StopExecutionException 异常。(联想到组件化在不一样的 moudle 中须要给资源加 prefix 前缀)
  • 二、 对插件进行初始化与配置相关信息
    • 1)、 当即执行插件初始化
    • 2)、 初始化 用于记录构建过程当中配置信息的工厂实例 ProcessProfileWriterFactory
    • 3)、 给 project 设置 android plugin version、插件类型、插件生成器、project 选项

二、configureProject 配置项目

Plugin 的准备工程完成以后,就会执行 BasePlugin 中的 configureProject 方法进行项目的配置了,其源码以下所示:

private void configureProject() {
        
        ...
        
        // 一、建立 DataBindingBuilder 实例。
        dataBindingBuilder = new DataBindingBuilder();
        dataBindingBuilder.setPrintMachineReadableOutput(
                SyncOptions.getErrorFormatMode(projectOptions) == ErrorFormatMode.MACHINE_PARSABLE);

        // 二、强制使用不低于当前所支持的最小插件版本,不然会抛出异常。
        GradlePluginUtils.enforceMinimumVersionsOfPlugins(project, syncIssueHandler);

        // 三、应用 Java Plugin。
        project.getPlugins().apply(JavaBasePlugin.class);

        // 四、若是启动了 构建缓存 选项,则会建立 buildCache 实例以便后面能重用缓存。
        @Nullable
        FileCache buildCache = BuildCacheUtils.createBuildCacheIfEnabled(project, projectOptions);

        // 五、这个回调将会在整个 project 执行完成以后执行(注意不是在当前 moudle 执行完成以后执行),由于每个 project 都会调用此回调, 因此它可能会执行屡次。
        // 在整个 project 构建完成以后,会进行资源回收、缓存清除并关闭在此过程当中全部启动的线程池组件。
        gradle.addBuildListener(
                new BuildAdapter() {
                    @Override
                    public void buildFinished(@NonNull BuildResult buildResult) {
                        // Do not run buildFinished for included project in composite build.
                        if (buildResult.getGradle().getParent() != null) {
                            return;
                        }
                        ModelBuilder.clearCaches();
                        Workers.INSTANCE.shutdown();
                        sdkComponents.unload();
                        SdkLocator.resetCache();
                        ConstraintHandler.clearCache();
                        CachedAnnotationProcessorDetector.clearCache();
                        threadRecorder.record(
                                ExecutionType.BASE_PLUGIN_BUILD_FINISHED,
                                project.getPath(),
                                null,
                                () -> {
                                    if (!projectOptions.get(
                                            BooleanOption.KEEP_SERVICES_BETWEEN_BUILDS)) {
                                        WorkerActionServiceRegistry.INSTANCE
                                                .shutdownAllRegisteredServices(
                                                        ForkJoinPool.commonPool());
                                    }
                                    Main.clearInternTables();
                                });
                        DeprecationReporterImpl.Companion.clean();
                    }
                });
        
        ...
}
复制代码

最后,咱们梳理下 configureProject 中所执行的 五项主要任务,以下所示:

  • 1)、 建立 DataBindingBuilder 实例
  • 2)、 强制使用不低于当前所支持的最小插件版本,不然会抛出异常
  • 3)、 应用 Java Plugin
  • 4)、 若是启动了 构建缓存 选项,则会建立 buildCache 实例以便后续能重用缓存
  • 5)、 这个回调将会在整个 project 执行完成以后执行(注意不是在当前 moudle 执行完成以后执行),由于每个 project 都会调用此回调, 因此它可能会执行屡次。最后,在整个 project 构建完成以后,会进行资源回收、缓存清除并关闭在此过程当中全部启动的线程池组件

三、configureExtension 配置 Extension

而后,咱们看到 BasePlugin 的 configureExtension 方法,其核心源码以下所示:

private void configureExtension() {
        
        // 一、建立盛放 buildType、productFlavor、signingConfig 的容器实例。
        ...

        final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs =
                project.container(BaseVariantOutput.class);

        // 二、建立名为 buildOutputs 的扩展属性配置。
        project.getExtensions().add("buildOutputs", buildOutputs);

        ...

        // 三、建立 android DSL 闭包。
        extension =
                createExtension(
                        project,
                        projectOptions,
                        globalScope,
                        buildTypeContainer,
                        productFlavorContainer,
                        signingConfigContainer,
                        buildOutputs,
                        sourceSetManager,
                        extraModelInfo);

        // 四、给全局域设置建立好的 android DSL 闭包。
        globalScope.setExtension(extension);

        // 五、建立一个 ApplicationVariantFactory 实例,以用于生产 APKs。
        variantFactory = createVariantFactory(globalScope);

        // 六、建立一个 ApplicationTaskManager 实例,负责为 Android 应用工程去建立 Tasks。
        taskManager =
                createTaskManager(
                        globalScope,
                        project,
                        projectOptions,
                        dataBindingBuilder,
                        extension,
                        variantFactory,
                        registry,
                        threadRecorder);

        // 七、建立一个 VariantManager 实例,用于去建立与管理 Variant。
        variantManager =
                new VariantManager(
                        globalScope,
                        project,
                        projectOptions,
                        extension,
                        variantFactory,
                        taskManager,
                        sourceSetManager,
                        threadRecorder);
                        
         // 八、将 whenObjectAdded callbacks 映射到 singingConfig 容器之中。
        signingConfigContainer.whenObjectAdded(variantManager::addSigningConfig);

        // 九、若是不是 DynamicFeature(负责添加一个可选的 APK 模块),则会初始化一个 debug signingConfig DSL 对象并设置给默认的 buildType DSL。
        buildTypeContainer.whenObjectAdded(
                buildType -> {
                    if (!this.getClass().isAssignableFrom(DynamicFeaturePlugin.class)) {
                        SigningConfig signingConfig =
                                signingConfigContainer.findByName(BuilderConstants.DEBUG);
                        buildType.init(signingConfig);
                    } else {
                        // initialize it without the signingConfig for dynamic-features.
                        buildType.init();
                    }
                    variantManager.addBuildType(buildType);
                });

        // 十、将 whenObjectAdded callbacks 映射到 productFlavor 容器之中。
        productFlavorContainer.whenObjectAdded(variantManager::addProductFlavor);

        // 十一、将 whenObjectRemoved 映射在容器之中,当 whenObjectRemoved 回调执行时,会抛出 UnsupportedAction 异常。
        signingConfigContainer.whenObjectRemoved(
                new UnsupportedAction("Removing signingConfigs is not supported."));
        buildTypeContainer.whenObjectRemoved(
                new UnsupportedAction("Removing build types is not supported."));
        productFlavorContainer.whenObjectRemoved(
                new UnsupportedAction("Removing product flavors is not supported."));

        // 十二、按顺序依次建立 signingConfig debug、buildType debug、buildType release 类型的 DSL。
        variantFactory.createDefaultComponents(
                buildTypeContainer, productFlavorContainer, signingConfigContainer);
}
复制代码

最后,咱们梳理下 configureExtension 中的任务,以下所示:

  • 1)、建立盛放 buildType、productFlavor、signingConfig 的容器实例。
  • 2)、建立名为 buildOutputs 的扩展属性配置。
  • 3)、建立 android DSL 闭包。
  • 4)、给全局域设置建立好的 android DSL 闭包。
  • 5)、建立一个 ApplicationVariantFactory 实例,以用于生产 APKs。
  • 6)、建立一个 ApplicationTaskManager 实例,负责为 Android 应用工程去建立 Tasks。
  • 7)、建立一个 VariantManager 实例,用于去建立与管理 Variant。
  • 8)、将 whenObjectAdded callbacks 映射到 singingConfig 容器之中。
  • 9)、将 whenObjectAdded callbacks 映射到 buildType 容器之中。若是不是 DynamicFeature(负责添加一个可选的 APK 模块),则会初始化一个 debug signingConfig DSL 对象并设置给默认的 buildType DSL。
  • 10)、将 whenObjectAdded callbacks 映射到 productFlavor 容器之中。
  • 11)、将 whenObjectRemoved 映射在容器之中,当 whenObjectRemoved 回调执行时,会抛出 UnsupportedAction 异常。
  • 12)、按顺序依次建立 signingConfig debug、buildType debug、buildType release 类型的 DSL。

其中 最核心的几项处理能够概括为以下 四点

  • 1)、 建立 AppExtension,即 build.gradle 中的 android DSL
  • 2)、 依次建立应用的 variant 工厂、Task 管理者,variant 管理者
  • 3)、 注册 新增/移除配置 的 callback,依次包括 signingConfig,buildType,productFlavor
  • 4)、 依次建立默认的 debug 签名、建立 debug 和 release 两个 buildType

在 BasePlugin 的 apply 方法最后,调用了 createTasks 方法来建立 Tasks,该方法以下所示:

 private void createTasks() {
        // 一、在 evaluate 以前建立 Tasks
        threadRecorder.record(
                ExecutionType.TASK_MANAGER_CREATE_TASKS,
                project.getPath(),
                null,
                () -> taskManager.createTasksBeforeEvaluate());

        // 二、建立 Android Tasks
        project.afterEvaluate(
                CrashReporting.afterEvaluate(
                        p -> {
                            sourceSetManager.runBuildableArtifactsActions();

                            threadRecorder.record(
                                    ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
                                    project.getPath(),
                                    null,
                                    this::createAndroidTasks);
                        }));
}
复制代码

能够看到,createTasks 分为两种 Task 的建立方式,即 createTasksBeforeEvaluate 与 createAndroidTasks

下面,咱们来详细分析下其实现过程。

四、TaskManager#createTasksBeforeEvaluate 建立不依赖 flavor 的 task

TaskManager 的 createTasksBeforeEvaluate 方法给 Task 容器中注册了一系列的 Task,包括 uninstallAllTask、deviceCheckTask、connectedCheckTask、preBuild、extractProguardFiles、sourceSetsTask、assembleAndroidTest、compileLintTask 等等

五、BasePlugin#createAndroidTasks 建立构建 task

在 BasePlugin 的 createAndroidTasks 方法中主要 是生成 flavors 相关数据,并根据 flavor 建立与之对应的 Task 实例并注册进 Task 容器之中。其核心源码以下所示:

 @VisibleForTesting
    final void createAndroidTasks() {
    
    // 一、CompileSdkVersion、插件配置冲突检测(如 JavaPlugin、retrolambda)。
    
    // 建立一些基础或通用的 Tasks。
    
    // 二、将 Project Path、CompileSdk、BuildToolsVersion
    、Splits、KotlinPluginVersion、FirebasePerformancePluginVersion 等信息写入 Project 的配置之中。
    ProcessProfileWriter.getProject(project.getPath())
                .setCompileSdk(extension.getCompileSdkVersion())
                .setBuildToolsVersion(extension.getBuildToolsRevision().toString())
                .setSplits(AnalyticsUtil.toProto(extension.getSplits()));
                
    String kotlinPluginVersion = getKotlinPluginVersion();
        if (kotlinPluginVersion != null) {
            ProcessProfileWriter.getProject(project.getPath())
                    .setKotlinPluginVersion(kotlinPluginVersion);
        }
        AnalyticsUtil.recordFirebasePerformancePluginVersion(project);
                
    // 三、建立应用的 Tasks。
    List<VariantScope> variantScopes = variantManager.createAndroidTasks();

    // 建立一些基础或通用的 Tasks 与作一些通用的处理。
    
}
复制代码

在 createAndroidTasks 除了建立一些基础或通用的 Tasks 与作一些通用的处理以外, 主要作了三件事,以下所示:

  • 1)、 CompileSdkVersion、插件配置冲突检测(如 JavaPlugin、retrolambda 插件)
  • 2)、 将 Project Path、CompileSdk、BuildToolsVersion、Splits、KotlinPluginVersion、FirebasePerformancePluginVersion 等信息写入 Project 的配置之中
  • 3)、 建立应用的 Tasks

咱们须要 重点关注 variantManager 的 createAndroidTasks 方法,去核心源码以下所示:

 /** Variant/Task creation entry point. */
    public List<VariantScope> createAndroidTasks() {
       
        ...
       
        // 一、建立工程级别的测试任务。
        taskManager.createTopLevelTestTasks(!productFlavors.isEmpty());

        // 二、遍历全部 variantScope,为其变体数据建立对应的 Tasks。
        for (final VariantScope variantScope : variantScopes) {
            createTasksForVariantData(variantScope);
        }

        // 三、建立报告相关的 Tasks。
        taskManager.createReportTasks(variantScopes);

        return variantScopes;
    }
复制代码

能够看到,在 createAndroidTasks 方法中有 三项处理,以下所示:

  • 1)、 建立工程级别的测试任务
  • 2)、 遍历全部的 variantScope,为其变体数据建立对应的 Tasks
  • 3)、 建立报告相关的 Tasks

接着,咱们继续看看 createTasksForVariantData 方法是如何为每个指定的 Variant 类型建立对应的 Tasks 的,其核心源码以下所示:

// 为每个指定的 Variant 类型建立与之对应的 Tasks
public void createTasksForVariantData(final VariantScope variantScope) {
        final BaseVariantData variantData = variantScope.getVariantData();
        final VariantType variantType = variantData.getType();
        final GradleVariantConfiguration variantConfig = variantScope.getVariantConfiguration();

        // 一、建立 Assemble Task。
        taskManager.createAssembleTask(variantData);
        
        // 二、若是 variantType 是 base moudle,则会建立相应的 bundle Task。须要注意的是,base moudle 是指包含功能的 moudle,而用于 test 的 moudle 则是不包含功能的。
        if (variantType.isBaseModule()) {
            taskManager.createBundleTask(variantData);
        }

        // 三、若是 variantType 是一个 test moudle(其做为一个 test 的组件),则会建立相应的 test variant。
        if (variantType.isTestComponent()) {
        
            // 1)、将 variant-specific, build type multi-flavor、defaultConfig 这些依赖添加到当前的 variantData 之中。
            ...
            
            // 2)、若是支持渲染脚本,则添加渲染脚本的依赖。
            if (testedVariantData.getVariantConfiguration().getRenderscriptSupportModeEnabled()) {
                project.getDependencies()
                        .add(
                                variantDep.getCompileClasspath().getName(),
                                project.files(
                                        globalScope
                                                .getSdkComponents()
                                                .getRenderScriptSupportJarProvider()));
            }
        
            // 3)、若是当前 Variant 会输出一个 APK,即当前是执行的一个 Android test(通常用来进行 UI 自动化测试),则会建立相应的 AndroidTestVariantTask。
            if (variantType.isApk()) { // ANDROID_TEST
                if (variantConfig.isLegacyMultiDexMode()) {
                    String multiDexInstrumentationDep =
                            globalScope.getProjectOptions().get(BooleanOption.USE_ANDROID_X)
                                    ? ANDROIDX_MULTIDEX_MULTIDEX_INSTRUMENTATION
                                    : COM_ANDROID_SUPPORT_MULTIDEX_INSTRUMENTATION;
                    project.getDependencies()
                            .add(
                                    variantDep.getCompileClasspath().getName(),
                                    multiDexInstrumentationDep);
                    project.getDependencies()
                            .add(
                                    variantDep.getRuntimeClasspath().getName(),
                                    multiDexInstrumentationDep);
                }

                taskManager.createAndroidTestVariantTasks(
                        (TestVariantData) variantData,
                        variantScopes
                                .stream()
                                .filter(TaskManager::isLintVariant)
                                .collect(Collectors.toList()));
            } else { // UNIT_TEST
                // 4)、不然说明该 Test moudle 是用于执行单元测试的,则会建立 UnitTestVariantTask。 taskManager.createUnitTestVariantTasks((TestVariantData) variantData);
            }

        } else {
            // 四、若是不是一个 Test moudle,则会调用 ApplicationTaskManager 的 createTasksForVariantScope 方法。
            taskManager.createTasksForVariantScope(
                    variantScope,
                    variantScopes
                            .stream()
                            .filter(TaskManager::isLintVariant)
                            .collect(Collectors.toList()));
        }
}
复制代码

在 createTasksForVariantData 方法中为每个指定的 Variant 类型建立了与之对应的 Tasks,该方法的处理逻辑以下所示:

  • 一、 建立 Assemble Task
  • 二、 若是 variantType 是 base moudle,则会建立相应的 bundle Task。须要注意的是,base moudle 是指包含功能的 moudle,而用于 test 的 moudle 则是不包含功能的
  • 三、 若是 variantType 是一个 test moudle(其做为一个 test 的组件),则会建立相应的 test variant
    • 1)、 将 variant-specific, build type multi-flavor、defaultConfig 这些依赖添加到当前的 variantData 之中
    • 2)、 若是支持渲染脚本,则添加渲染脚本的依赖
    • 3)、 若是当前 Variant 会输出一个 APK,即当前是执行的一个 Android test(通常用来进行 UI 自动化测试),则会建立相应的 AndroidTestVariantTask
    • 4)、 不然说明该 Test moudle 是用于执行单元测试的,则会建立 UnitTestVariantTask
  • 四、 若是不是一个 Test moudle,则会调用 ApplicationTaskManager 的 createTasksForVariantScope 方法

最终,会执行到 ApplicationTaskManager 的 createTasksForVariantScope 方法,在这个方法里面建立了适用于应用构建的一系列 Tasks

下面,咱们就经过 assembleDebug 的打包流程来分析一下这些 Tasks。

6、assembleDebug 打包流程浅析

在对 assembleDebug 构建过程当中的一系列 Task 分析以前,咱们须要先回顾一下 Android 的打包流程(对这块很是熟悉的同窗能够跳过)。

一、Android 打包流程回顾

Android 官方的编译打包流程图以下所示:

比较粗略的打包流程可简述为以下 四个步骤

  • 1)、 编译器会将 APP 的源代码转换成 DEX(Dalvik Executable) 文件(其中包括 Android 设备上运行的字节码),并将全部其余内容转换成已编译资源
  • 2)、 APK 打包器将 DEX 文件和已编译的资源合并成单个 APK。 可是,必须先签署 APK,才能将应用安装并部署到 Android 设备上
  • 3)、 APK 打包器会使用相应的 keystore 发布密钥库去签署 APK
  • 4)、 在生成最终的 APK 以前,打包器会使用 zipalign 工具对应用进行优化,减小其在设备上运行时占用的内存

为了 了解更多打包过程当中的细节,咱们须要查看更加详细的旧版 APK 打包流程图 ,以下图所示:

比较详细的打包流程可简述为以下 八个步骤

  • 一、 首先,.aidl(Android Interface Description Language)文件须要经过 aidl 工具转换成编译器可以处理的 Java 接口文件
  • 二、 同时,资源文件(包括 AndroidManifest.xml、布局文件、各类 xml 资源等等)将被 AAPT(Asset Packaging Tool)(Android Gradle Plugin 3.0.0 及以后使用 AAPT2 替代了 AAPT)处理为最终的 resources.arsc,并生成 R.java 文件以保证源码编写时能够方便地访问到这些资源
  • 三、 而后,经过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,最终它们会统一被编译成 .class 文件
  • 四、 由于 .class 并非 Android 系统所能识别的格式,因此还须要经过 dex 工具将它们转化为相应的 Dalvik 字节码(包含压缩常量池以及清除冗余信息等工做)。这个过程当中还会加入应用所依赖的全部 “第三方库”
  • 五、 下一步,经过 ApkBuilder 工具将资源文件、DEX 文件打包生成 APK 文件
  • 六、 接着,系统将上面生成的 DEX、资源包以及其它资源经过 apkbuilder 生成初始的 APK 文件包
  • 七、 而后,经过签名工具 Jarsigner 或者其它签名工具对 APK 进行签名获得签名后的 APK。若是是在 Debug 模式下,签名所用的 keystore 是系统自带的默认值,不然咱们须要提供本身的私钥以完成签名过程
  • 八、 最后,若是是正式版的 APK,还会利用 ZipAlign 工具进行对齐处理,以提升程序的加载和运行速度。而对齐的过程就是将 APK 文件中全部的资源文件距离文件的起始位置都偏移4字节的整数倍,这样经过 mmap 访问 APK 文件的速度会更快,而且会减小其在设备上运行时的内存占用

至此,咱们已经了解了整个 APK 编译和打包的流程。

那么,为何 XML 资源文件要从文本格式编译成二进制格式?

主要基于如下 两点缘由

  • 一、空间占用更小由于全部 XML 元素的标签、属性名称、属性值和内容所涉及到的字符串都会被统一收集到一个字符串资源池中,而且会去重。有了这个字符串资源池,原来使用字符串的地方就会被替换成一个索引到字符串资源池的整数值,从而能够减小文件的大小

  • 二、解析效率更高二进制格式的 XML 文件解析速度更快。这是因为二进制格式的 XML 元素里面再也不包含有字符串值,所以就避免了进行字符串解析,从而提升了解析效率

而 Android 资源管理框架又是如何快速定位到最匹配资源的?

主要基于两个文件,以下所示:

  • 一、 资源 ID 文件 R.java赋予每个非 assets 资源一个 ID 值,这些 ID 值以常量的形式定义在 R.java 文件中
  • 二、 资源索引表 resources.arsc用来描述那些具备 ID 值的资源的配置信息

二、assmableDebug 打包流程浅析

咱们能够经过下面的命令来获取打包一个 Debug APK 所须要的执行的 Task,以下所示:

quchao@quchaodeMacBook-Pro CustomPlugin % ./gradlew app:assembleDebug --console=plain

...

> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:generateDebugBuildConfig
> Task :app:javaPreCompileDebug
> Task :app:mainApkListPersistenceDebug
> Task :app:generateDebugResValues
> Task :app:createDebugCompatibleScreenManifests
> Task :app:extractDeepLinksDebug
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:generateDebugResources
> Task :app:processDebugManifest
> Task :app:mergeDebugResources
> Task :app:processDebugResources
> Task :app:compileDebugJavaWithJavac
> Task :app:compileDebugSources
> Task :app:mergeDebugShaders
> Task :app:compileDebugShaders
> Task :app:generateDebugAssets
> Task :app:mergeDebugAssets
> Task :app:processDebugJavaRes NO-SOURCE
> Task :app:checkDebugDuplicateClasses
> Task :app:dexBuilderDebug
> Task :app:mergeLibDexDebug
> Task :app:mergeDebugJavaResource
> Task :app:mergeDebugJniLibFolders
> Task :app:validateSigningDebug
> Task :app:mergeProjectDexDebug
> Task :app:mergeDebugNativeLibs
> Task :app:stripDebugDebugSymbols
> Task :app:desugarDebugFileDependencies
> Task :app:mergeExtDexDebug
> Task :app:packageDebug
> Task :app:assembleDebug
复制代码

在 TaskManager 中,主要有两种方法用来去建立 Task,它们分别为 createTasksBeforeEvaluate 方法与 createTasksForVariantScope 方法。须要注意的是,createTasksForVariantScope 方法是一个抽象方法,其具体的建立 Tasks 的任务分发给了 TaskManager 的子类进行处理,其中最多见的子类要数 ApplicationTaskManager 了,它就是在 Android 应用程序中用于建立 Tasks 的 Task 管理者

其中,打包流程中的大部分 tasks 都在这个目录之下:

com.android.build.gradle.internal.tasks

下面,咱们看看 assembleDebug 打包流程中所需的各个 Task 所对应的实现类与含义,以下表所示:

Task 对应实现类 做用
preBuild AppPreBuildTask 预先建立的 task,用于作一些 application Variant 的检查
preDebugBuild 与 preBuild 区别是这个 task 是用于在 Debug 的环境下的一些 Vrariant 检查
generateDebugBuildConfig GenerateBuildConfig 生成与构建目标相关的 BuildConfig 类
javaPreCompileDebug JavaPreCompileTask 用于在 Java 编译以前执行必要的 action
mainApkListPersistenceDebug MainApkListPersistence 用于持久化 APK 数据
generateDebugResValues GenerateResValues 生成 Res 资源类型值
createDebugCompatibleScreenManifests CompatibleScreensManifest 生成具备给定屏幕密度与尺寸列表的 (兼容屏幕)节点清单
extractDeepLinksDebug ExtractDeepLinksTask 用于抽取一系列 DeepLink(深度连接技术,主要应用场景是经过Web页面直接调用Android原生app,而且把须要的参数经过Uri的形式,直接传递给app,节省用户的注册成本)
compileDebugAidl AidlCompile 编译 AIDL 文件
compileDebugRenderscript RenderscriptCompile 编译 Renderscript 文件
generateDebugResources 在 TaskManager.createAnchorTasks 方法中经过 taskFactory.register(taskName)的方式注册一个 task 空 task,锚点
processDebugManifest ProcessApplicationManifest 处理 manifest 文件
mergeDebugResources MergeResources 使用 AAPT2 合并资源文件
processDebugResources ProcessAndroidResources 用于处理资源并生成 R.class 文件
compileDebugJavaWithJavac JavaCompileCreationAction(这里是一个 Action,从 gradle 源码中能够看到从 TaskFactory 中注册一个 Action 能够获得与之对应的 Task,所以,Task 即 Action,Action 即 Task) 用于执行 Java 源码的编译
compileDebugSources 在 TaskManager.createAnchorTasks 方法中经过 taskFactory.register(taskName)的方式注册一个 task 空 task,锚点使用
mergeDebugShaders MergeSourceSetFolders.MergeShaderSourceFoldersCreationAction 合并 Shader 文件
compileDebugShaders ShaderCompile 编译 Shaders
generateDebugAssets 在 TaskManager.createAnchorTasks 方法中经过 taskFactory.register(taskName)的方式注册一个 task 空 task,锚点
mergeDebugAssets MergeSourceSetFolders.MergeAppAssetCreationAction 合并 assets 文件
processDebugJavaRes ProcessJavaResConfigAction 处理 Java Res 资源
checkDebugDuplicateClasses CheckDuplicateClassesTask 用于检测工程外部依赖,确保不包含重复类
dexBuilderDebug DexArchiveBuilderTask 用于将 .class 文件转换成 dex archives,即 DexArchive,Dex 存档,能够经过 addFile 添加一个 DEX 文件
mergeLibDexDebug DexMergingTask.DexMergingAction.MERGE_LIBRARY_PROJECT 仅仅合并库工程中的 DEX 文件
mergeDebugJavaResource MergeJavaResourceTask 合并来自多个 moudle 的 Java 资源
mergeDebugJniLibFolders MergeSourceSetFolders.MergeJniLibFoldersCreationAction 以合适的优先级合并 JniLibs 源文件夹
validateSigningDebug ValidateSigningTask 用于检查当前 Variant 的签名配置中是否存在密钥库文件,若是当前密钥库默认是 debug keystore,即便它不存在也会进行相应的建立
mergeProjectDexDebug DexMergingTask.DexMergingAction.MERGE_PROJECT 仅仅合并工程的 DEX 文件
mergeDebugNativeLibs MergeNativeLibsTask 从多个 moudle 中合并 native 库
stripDebugDebugSymbols StripDebugSymbolsTask 从 Native 库中移除 Debug 符号。
desugarDebugFileDependencies DexFileDependenciesTask 处理 Dex 文件的依赖关系
mergeExtDexDebug DexMergingTask.DexMergingAction.MERGE_EXTERNAL_LIBS 仅仅用于合并外部库的 DEX 文件
packageDebug PackageApplication 打包 APK
assembleDebug Assemble 空 task,锚点使用

目前,在 Gradle Plugin 中主要有三种类型的 Task,以下所示:

  • 1)、 增量 Task继承于 NewIncrementalTask 这个增量 Task 基类,须要重写 doTaskAction 抽象方法实现增量功能
  • 2)、 非增量 Task继承于 NonIncrementalTask 这个非增量 Task 基类,重写 doTaskAction 抽象方法实现全量更新功能
  • 3)、 Transform Task咱们编写的每个自定义 Transform 会在调用 appExtension.registerTransform(new CustomTransform()) 注册方法时将其保存到当前的 Extension 类中的 transforms 列表中,当 LibraryTaskManager/TaskManager 调用 createPostCompilationTasks(负责为给定 Variant 建立编译后的 task)方法时,会取出相应 Extension 中的 tranforms 列表进行遍历,并经过 TransformManager.addTransform 方法将每个 Transform 转换为与之对应的 TransformTask 实例,而该方法内部具体是经过 new TransformTask.CreationAction(...) 的形式进行建立

全面了解了打包过程当中涉及到的一系列 Tasks 与 Task 必备的一些基础知识以后,咱们再来对其中最重要的几个 Task 的实现来进行详细分析。

7、重要 Task 实现源码分析

一、资源处理相关 Task

1)、processDebugManifest

processDebugManifest 对应的实现类为 ProcessApplicationManifest Task,它继承了 IncrementalTask,可是没有实现 isIncremental 方法,所以咱们只需看其 doFullTaskAction 方法便可。

调用链路

processDebugManifest.dofFullTaskAction => ManifestHelperKt.mergeManifestsForApplication => ManifestMerge2.merge
复制代码

主要流程分析

这个 task 功能主要是 用于合并全部的(包括 module 和 flavor) mainfest,其过程主要是利用 MergingReport,ManifestMerger2 和 XmlDocument 这三个实例进行处理

咱们直接关注到 ManifestMerger2.merge 方法的 merge 过程,看看具体的合并是怎样的。其主体步骤以下所示:

一、获取主 manifest 的信息,以作一些必要的检查,这里会返回一个 LoadedManifestInfo 实例。
// load the main manifest file to do some checking along the way.
LoadedManifestInfo loadedMainManifestInfo =
        load(
                new ManifestInfo(
                        mManifestFile.getName(),
                        mManifestFile,
                        mDocumentType,
                        Optional.absent() /* mainManifestPackageName*/),
                selectors,
                mergingReportBuilder);
复制代码
二、执行 Manifest 中的系统属性注入:将主 Manifest 中定义的某些属性替换成 gradle 中定义的属性,例如 package, version_code, version_name, min_sdk_versin 、target_sdk_version、max_sdk_version 等等。
// perform system property injection
performSystemPropertiesInjection(mergingReportBuilder,
        loadedMainManifestInfo.getXmlDocument());
复制代码
/**
 * Perform {@link ManifestSystemProperty} injection.
 * @param mergingReport to log actions and errors.
 * @param xmlDocument the xml document to inject into.
 */

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);
        }
    }
}
复制代码
三、合并 flavors 而且构建与之对应的 manifest 文件。
for (File inputFile : mFlavorsAndBuildTypeFiles) {
    mLogger.verbose("Merging flavors and build manifest %s \n", inputFile.getPath());
    LoadedManifestInfo overlayDocument =
            load(
                    new ManifestInfo(
                            null,
                            inputFile,
                            XmlDocument.Type.OVERLAY,
                            mainPackageAttribute.transform(it -> it.getValue())),
                    selectors,
                    mergingReportBuilder);
    if (!mFeatureName.isEmpty()) {
        overlayDocument =
                removeDynamicFeatureManifestSplitAttributeIfSpecified(
                        overlayDocument, mergingReportBuilder);
    }
    // 一、检查 package 定义
    Optional<XmlAttribute> packageAttribute =
            overlayDocument.getXmlDocument().getPackage();
    // if both files declare a package name, it should be the same.
    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());
        mergingReportBuilder.addMessage(
                overlayDocument.getXmlDocument().getSourceFile(),
                MergingReport.Record.Severity.ERROR,
                message);
        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 文件中的 placeholder 替换
performPlaceHolderSubstitution(
        loadedMainManifestInfo,
        xmlDocumentOptional.get(),
        mergingReportBuilder,
        severity);
复制代码
六、以后对最终合并后的 manifest 中的一些属性进行一次替换,与步骤 2 相似。
七、保存 manifest 到 build/intermediates/merged_manifests/flavorName/AndroidManifest.xml,至此,已生成最终的 Manifest 文件。

2)、mergeDebugResources

mergeDebugResources 对应的是 MergeResources Task,它 使用了 AAPT2 合并资源

调用链路

MergeResources.doFullTaskAction => ResourceMerger.mergeData => MergedResourceWriter.end => mResourceCompiler.submitCompile => AaptV2CommandBuilder.makeCompileCommand
复制代码

主体流程分析

MergeResources 继承自 IncrementalTask,对于 增量 Task 来讲咱们只需看以下三个方法的实现:

  • isIncremental
  • doFullTaskAction
  • doIncrementalTaskAction
一、首先查看 isIncremental 方法。
    // 说明 MergeResources Task 支持增量,确定重写了 doIncrementalTaskAction 方法
    protected boolean isIncremental() {
        return true;
    }
复制代码
二、而后,查看 doFullTaskAction 方法,内部经过 getConfiguredResourceSets 方法获取了 resourceSets,包括了本身的 res 和依赖库的 res 资源以及 build/generated/res/rs。
List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor);
复制代码
三、建立 ResourceMerger,并使用 resourceSets 进行填充。
ResourceMerger merger = new ResourceMerger(minSdk.get());
复制代码
四、建立 ResourceCompilationService,它使用了 aapt2。
// makeAapt 中使用 aapt2,而后返回 ResourceCompilationService 实例.
ResourceCompilationService resourceCompiler =
        getResourceProcessor(
                getAapt2FromMaven(),
                workerExecutorFacade,
                errorFormatMode,
                flags,
                processResources,
                getLogger())) {
复制代码
五、将第 2 步获取的 resourceSet 加入至 ResourceMerger 中。
for (ResourceSet resourceSet : resourceSets) {
    resourceSet.loadFromFiles(new LoggerWrapper(getLogger()));
    merger.addDataSet(resourceSet);
}
复制代码
六、建立 MergedResourceWriter
MergedResourceWriter writer =
        new MergedResourceWriter(
                workerExecutorFacade,
                destinationDir,
                publicFile,
                mergingLog,
                preprocessor,
                resourceCompiler,
                getIncrementalFolder(),
                dataBindingLayoutProcessor,
                mergedNotCompiledResourcesOutputDirectory,
                pseudoLocalesEnabled,
                getCrunchPng());
复制代码
七、调用 ResourceMerger.mergeData 方法对资源进行合并。
merger.mergeData(writer, false /*doCleanUp*/);
复制代码
八、调用 MergedResourceWriter 的 start,ignoreItemInMerge、removeItem、addItem,end 方法,其中 item 中包括了须要处理的资源,包括 xml 和 图片资源,每个 item 对应的文件,都会建立一个与之对应的 CompileResourceRequest 实例,并加入到 mCompileResourceRequests 这个 ConcurrentLinkedQueue 队列中。
九、调用 mResourceCompiler.submitCompile 方法处理资源。
// MergedResourceWriter.end()
mResourceCompiler.submitCompile(
        new CompileResourceRequest(
                fileToCompile,
                request.getOutputDirectory(),
                request.getInputDirectoryName(),
                request.getInputFileIsFromDependency(),
                pseudoLocalesEnabled,
                crunchPng,
                ImmutableMap.of(),
                request.getInputFile()));
mCompiledFileMap.put(
        fileToCompile.getAbsolutePath(),
        mResourceCompiler.compileOutputFor(request).getAbsolutePath());
复制代码

在 submitCompile 中最终会使用 AaptV2CommandBuilder.makeCompileCommand 方法生成 aapt2 命令去处理资源。

十、最后,对 doIncrementalTaskAction 的实现我这里就不赘述了,由于增量 task 的实现过程和全量实现差别不大,仅仅是使用修改后的文件去获取 resourceSets 。

二、将 Class 文件打包成 Dex 文件的过程

即 dexBuilderDebug,它具体对应的是 DexArchiveBuilderTask,用于将 .class 文件转换成 dex archives,即 Dex 存档,它能够经过 addFile 添加一个 DEX 文件。

调用链路

DexArchiveBuilderTask.doTaskAction => DexArchiveBuilderTaskDelegate.doProcess => DexArchiveBuilderTaskDelegate.processClassFromInput => DexArchiveBuilderTaskDelegate.convertToDexArchive -> DexArchiveBuilderTaskDelegate.launchProcessing -> DexArchiveBuilder.convert
复制代码

主体流程分析

在 DexArchiveBuilderTask 中,对 class 的处理方式分为两种,一种是对 目录下的 class 进行处理,一种是对 .jar 里面的 class 进行处理

那么,这里为何要分为这两种方式呢?

由于 .jar 中的 class 文件一般来讲都是依赖库,基本上不会改变,因此 gradle 在这里就能够实现一个缓存操做

一、convertJarToDexArchive 处理 jar

在处理 jar 包的时候,Gradle 会对 jar 包中的每个 class 文件都单独打成一个 DEX 文件,而后再把它们放回 jar 包之中。

private fun convertJarToDexArchive(
    jarInput: File,
    outputDir: File,
    bootclasspath: ClasspathServiceKey,
    classpath: ClasspathServiceKey,
    cacheInfo: D8DesugaringCacheInfo
): List<File> {
    if (cacheInfo !== DesugaringDontCache) {
        val cachedVersion = cacheHandler.getCachedVersionIfPresent(
            jarInput, cacheInfo.orderedD8DesugaringDependencies
        )
        if (cachedVersion != null) {
            // 若是有缓存,直接使用缓存的 jar 包。
            val outputFile = getOutputForJar(jarInput, outputDir, null)
            Files.copy(
                cachedVersion.toPath(),
                outputFile.toPath(),
                StandardCopyOption.REPLACE_EXISTING
            )
            // no need to try to cache an already cached version.
            return listOf()
        }
    }
    // 若是没有缓存,则调用 convertToDexArchive 方法去生成 dex。
    return convertToDexArchive(
        jarInput,
        outputDir,
        false,
        bootclasspath,
        classpath,
        setOf(),
        setOf()
    )
}
复制代码
二、使用 convertToDexArchive 处理 dir 以及 jar 的后续处理

内部会调用 launchProcessing 对 dir 进行处理,代码以下所示:

private fun launchProcessing(
    dexConversionParameters: DexArchiveBuilderTaskDelegate.DexConversionParameters,
    outStream: OutputStream,
    errStream: OutputStream,
    receiver: MessageReceiver
) {
    val dexArchiveBuilder = dexConversionParameters.getDexArchiveBuilder(
        outStream,
        errStream,
        receiver
    )
    val inputPath = dexConversionParameters.input.toPath()
    val hasIncrementalInfo =
        dexConversionParameters.input.isDirectory && dexConversionParameters.isIncremental
     // 若是 class 新增 || 修改过,就进行处理    
    fun toProcess(path: String): Boolean {
        if (!dexConversionParameters.belongsToThisBucket(path)) return false
        if (!hasIncrementalInfo) {
            return true
        }
        val resolved = inputPath.resolve(path).toFile()
        return resolved in dexConversionParameters.additionalPaths || resolved in dexConversionParameters.changedFiles
    }
    val bucketFilter = { name: String -> toProcess(name) }
    loggerWrapper.verbose("Dexing '" + inputPath + "' to '" + dexConversionParameters.output + "'")
    try {
        ClassFileInputs.fromPath(inputPath).use { input ->
            input.entries(bucketFilter).use { entries ->
                // 内部会调用 dx || d8 去生成 dex 文件
                dexArchiveBuilder.convert(
                    entries,
                    Paths.get(URI(dexConversionParameters.output)),
                    dexConversionParameters.input.isDirectory
                )
            }
        }
    } catch (ex: DexArchiveBuilderException) {
        throw DexArchiveBuilderException("Failed to process $inputPath", ex)
    }
}
复制代码

能够看到,在 DexArchiveBuilder 有两个子类,它们分别以下所示:

  • D8DexArchiveBuilder调用 D8 去生成 DEX 文件
  • DxDexArchiveBuilder调用 DX 去生成 DEX 文件

咱们这里就以 D8DexArchiveBuilder 为例来讲明其是如何调用 D8 去生成 DEX 文件的。其源码以下所示:

@Override
public void convert(
        @NonNull Stream<ClassFileEntry> input, @NonNull Path output, boolean isIncremental)

        throws DexArchiveBuilderException 
{
    // 一、建立一个 D8 诊断信息处理器实例,用于发出不一样级别的诊断信息,共分为三类,由严重程度递减分别为:error、warning、info。
    D8DiagnosticsHandler d8DiagnosticsHandler = new InterceptingDiagnosticsHandler();
    try {
        // 二、建立一个 D8 命令构建器实例。
        D8Command.Builder builder = D8Command.builder(d8DiagnosticsHandler);
        AtomicInteger entryCount = new AtomicInteger();
        
        // 三、遍历读取每个类的字节数据。
        input.forEach(
                entry -> {
                    builder.addClassProgramData(
                            readAllBytes(entry), D8DiagnosticsHandler.getOrigin(entry));
                    entryCount.incrementAndGet();
                });
        if (entryCount.get() == 0) {
            // 三、若是没有可遍历的数据,则直接 return。这里使用 AtomicInteger 类来实现了是否有遍历了数据的区分处理。
            return;
        }
        OutputMode outputMode =
                isIncremental ? OutputMode.DexFilePerClassFile : OutputMode.DexIndexed;
                
        // 四、给 D8 命令构建器实例设置一系列的配置,例如 编译模式、最小 Sdk 版本等等。
        builder.setMode(compilationMode)
                .setMinApiLevel(minSdkVersion)
                .setIntermediate(true)
                .setOutput(output, outputMode)
                .setIncludeClassesChecksum(compilationMode == compilationMode.DEBUG);
        if (desugaring) {
            builder.addLibraryResourceProvider(bootClasspath.getOrderedProvider());
            builder.addClasspathResourceProvider(classpath.getOrderedProvider());
            if (libConfiguration != null) {
                builder.addSpecialLibraryConfiguration(libConfiguration);
            }
        } else {
            builder.setDisableDesugaring(true);
        }
        
        // 五、使用 com.android.tools.r8 工具包中的 D8 类的 run 方法运行组装后的 D8 命令。
        D8.run(builder.build(), MoreExecutors.newDirectExecutorService());
    } catch (Throwable e) {
        throw getExceptionToRethrow(e, d8DiagnosticsHandler);
    }
}
复制代码

D8DexArchiveBuilder 的 convert 过程能够概括为 五个步骤,以下所示:

  • 1)、 建立一个 D8 诊断信息处理器实例,用于发出不一样级别的诊断信息,共分为三类,由严重程度递减分别为:error、warning、info
  • 2)、 建立一个 D8 命令构建器实例
  • 3)、 遍历读取每个类的字节数据
  • 4)、 给 D8 命令构建器实例设置一系列的配置,例如 编译模式、最小 Sdk 版本等等
  • 5)、 使用 com.android.tools.r8 工具包中的 D8 类的 run 方法运行组装后的 D8 命令

8、总结

咱们再回头看看开篇时的那一幅 Gradle 插件的总体实现架构图,以下所示:

最后的最后

咱们能够 根据上面这幅图,由下而上细细地思考回忆一下,每一层的主要流程是什么?其中涉及的一些关键细节具体有哪些?此时,你是否以为已经真正地理解了 Gradle 插件架构的实现原理呢

参考连接:


一、Android Gradle Plugin V3.6.2 源码

二、Gradle V5.6.4 源码

三、Android Plugin DSL Reference

四、Gradle DSL Reference

五、designing-gradle-plugins

六、android-training => gradle

七、连载 | 深刻理解Gradle框架之一:Plugin, Extension, buildSrc

八、连载 | 深刻理解gradle框架之二:依赖实现分析

九、连载 | 深刻理解gradle框架之三:artifacts的发布

十、Android Gradle Plugin 源码解析(上)

十一、Android Gradle Plugin 源码解析(下)

十二、Gradle 庖丁解牛(构建源头源码浅析)

1三、Gradle 庖丁解牛(构建生命周期核心委托对象建立源码浅析)

Contanct Me

● 微信:

欢迎关注个人微信:bcce5360

● 微信群:

因为微信群已超过 200 人,麻烦你们想进微信群的朋友们,加我微信拉你进群。

● QQ群:

2千人QQ群,Awesome-Android学习交流群,QQ群号:959936182, 欢迎你们加入~

About me

很感谢您阅读这篇文章,但愿您能将它分享给您的朋友或技术群,这对我意义重大。

但愿咱们能成为朋友,在 Github掘金上一块儿分享知识。

相关文章
相关标签/搜索