写点什么

深入学习 -Gradle- 自动化构建技术(五)Gradle- 插件架构实现原理剖析 -

用户头像
Android架构
关注
发布于: 2021 年 11 月 06 日

dataBindingBuilder = new DataBindingBuilder();dataBindingBuilder.setPrintMachineReadableOutput(SyncOptions.getErrorFormatMode(projectOptions) == ErrorFormatMode.MACHINE_PARSABLE);


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


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


// 4、如果启动了 构建缓存 选项,则会创建 buildCache 实例以便后面能重用缓存。@NullableFileCache buildCache = BuildCacheUtils.createBuildCacheIfEnabled(project, projectOptions);


// 5、这个回调将会在整个 project 执行完成之后执行(注意不是在当前 moudle 执行完成之后执行),因为每一个 project 都会调用此回调, 所以它可能会执行多次。// 在整个 project 构建完成之后,会进行资源回收、缓存清除并关闭在此过程中所有启动的线程池组件。gradle.addBuildListener(new BuildAdapter() {@Overridepublic 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 构建完成之后,会进行资源回收、缓存清除并关闭在此过程中所有启动的线程池组件

3、configureExtension 配置 Extension

然后,我们看到 BasePlugin 的 configureExtension 方法,其核心源码如下所示:


private void configureExtension() {


// 1、创建盛放 buildType、productFlavor、signingConfig 的容器实例。...


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


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


...


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


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


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


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


// 7、创建一个 VariantManager 实例,用于去创建与管理 Variant。variantManager =new VariantManager(globalScope,project,projectOptions,extension,variantFactory,taskManager,sourceSetManager,threadRecorder);


// 8、将 whenObjectAdded callbacks 映射到 singingConfig 容器之中。signingConfigContainer.whenObjectAdded(variantManager::addSigningConfig);


// 9、如果不是 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);});


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


// 11、将 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."));


// 12、按顺序依次创建 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() {// 1、在 evaluate 之前创建 TasksthreadRecorder.record(ExecutionType.TASK_MANAGER_CREATE_TASKS,project.getPath(),null,() -> taskManager.createTasksBeforeEvaluate());


// 2、创建 Android Tasksproject.afterEvaluate(CrashReporting.afterEvaluate(p -> {sourceSetManager.runBuildableArtifactsActions();


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


可以看到,createTasks 分为两种 Task 的创建方式,即 createTasksBeforeEvaluate 与 createAndroidTasks


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

4、TaskManager#createTasksBeforeEvaluate 创建不依赖 flavor 的 task

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

5、BasePlugin#createAndroidTasks 创建构建 task

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


@VisibleForTestingfinal void createAndroidTasks() {


// 1、CompileSdkVersion、插件配置冲突检测(如 JavaPlugin、retrolambda)。


// 创建一些基础或通用的 Tasks。


// 2、将 Project Path、CompileSdk、BuildToolsVersion、Splits、KotlinPluginVersion、FirebasePerformancePluginVersion 等信息写入 Project 的配置之中。ProcessProfileWriter.


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


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);


// 3、创建应用的 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() {


...


// 1、创建工程级别的测试任务。taskManager.createTopLevelTestTasks(!productFlavors.isEmpty());


// 2、遍历所有 variantScope,为其变体数据创建对应的 Tasks。for (final VariantScope variantScope : variantScopes) {createTasksForVariantData(variantScope);}


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


return variantScopes;}


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


  • 1)、创建工程级别的测试任务

  • 2)、遍历所有的 variantScope,为其变体数据创建对应的 Tasks

  • 3)、创建报告相关的 Tasks


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


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


// 1、创建 Assemble Task。taskManager.createAssembleTask(variantData);


// 2、如果 variantType 是 base moudle,则会创建相应的 bundle Task。需要注意的是,base moudle 是指包含功能的 moudle,而用于 test 的 moudle 则是不包含功能的。if (variantType.isBaseModule()) {taskManager.createBundleTask(variantData);}


// 3、如果 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_TESTif (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 {// 4、如果不是一个 Test moudle,则会调用 ApplicationTaskManager 的 createTasksForVariantScope 方法。taskManager.createTasksForVariantScope(variantScope,variantScopes.stream().filter(TaskManager::isLintVariant).collect(Collectors.toList()));}}


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


  • 1、创建 Assemble Task

  • 2、如果 variantType 是 base moudle,则会创建相应的 bundle Task。需要注意的是,base moudle 是指包含功能的 moudle,而用于 test 的 moudle 则是不包含功能的

  • 3、如果 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

  • 4、如果不是一个 Test moudle,则会调用 ApplicationTaskManager 的 createTasksForVariantScope 方法


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


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

六、assembleDebug 打包流程浅析

在对 assembleDebug 构建过程中的一系列 Task 分析之前,我们需要先回顾一下 Android 的打包流程(对这块非常熟悉的同学可以跳过)。

1、Android 打包流程回顾

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



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


  • 1)、编译器会将 APP 的源代码转换成 DEX(Dalvik Executable) 文件(其中包括 Android 设备上运行的字节码),并将所有其他内容转换成已编译资源

  • 2)、APK 打包器将 DEX 文件和已编译的资源合并成单个 APK。 但是,必须先签署 APK,才能将应用安装并部署到 Android 设备上

  • 3)、APK 打包器会使用相应的 keystore 发布密钥库去签署 APK

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


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



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


  • 1、首先,.aidl(Android Interface Description Language)文件需要通过 aidl 工具转换成编译器能够处理的 Java 接口文件

  • 2、同时,资源文件(包括 AndroidManifest.xml、布局文件、各种 xml 资源等等)将被 AAPT(Asset Packaging Tool)(Android Gradle Plugin 3.0.0 及之后使用 AAPT2 替代了 AAPT)处理为最终的 resources.arsc,并生成 R.java 文件以保证源码编写时可以方便地访问到这些资源

  • 3、然后,通过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,最终它们会统一被编译成 .class 文件

  • 4、因为 .class 并不是 Android 系统所能识别的格式,所以还需要通过 dex 工具将它们转化为相应的 Dalvik 字节码(包含压缩常量池以及清除冗余信息等工作)。这个过程中还会加入应用所依赖的所有 “第三方库”

  • 5、下一步,通过 ApkBuilder 工具将资源文件、DEX 文件打包生成 APK 文件

  • 6、接着,系统将上面生成的 DEX、资源包以及其它资源通过 apkbuilder 生成初始的 APK 文件包

  • 7、然后,通过签名工具 Jarsigner 或者其它签名工具对 APK 进行签名得到签名后的 APK。如果是在 Debug 模式下,签名所用的 keystore 是系统自带的默认值,否则我们需要提供自己的私钥以完成签名过程

  • 8、最后,如果是正式版的 APK,还会利用 ZipAlign 工具进行对齐处理,以提高程序的加载和运行速度。而对齐的过程就是将 APK 文件中所有的资源文件距离文件的起始位置都偏移 4 字节的整数倍,这样通过 mmap 访问 APK 文件的速度会更快,并且会减少其在设备上运行时的内存占用


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

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

主要基于以下 两点原因


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

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

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

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


  • 1、资源 ID 文件 R.java赋予每一个非 assets 资源一个 ID 值,这些 ID 值以常量的形式定义在 R.java 文件中

  • 2、资源索引表 resources.arsc用来描述那些具有 ID 值的资源的配置信息

2、assmableDebug 打包流程浅析

我们可以通过下面的命令来获取打包一个 Debug APK 所需要的执行的 Task,如下所示:


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


...


Task :app:preBuild UP-TO-DATETask :app:preDebugBuild UP-TO-DATETask :app:generateDebugBuildConfigTask :app:javaPreCompileDebugTask :app:mainApkListPersistenceDebugTask :app:generateDebugResValuesTask :app:createDebugCompatibleScreenManifestsTask :app:extractDeepLinksDebugTask :app:compileDebugAidl NO-SOURCETask :app:compileDebugRenderscript NO-SOURCETask :app:generateDebugResourcesTask :app:processDebugManifestTask :app:mergeDebugResourcesTask :app:processDebugResourcesTask :app:compileDebugJavaWithJavacTask :app:compileDebugSourcesTask :app:mergeDebugShadersTask :app:compileDebugShadersTask :app:generateDebugAssetsTask :app:mergeDebugAssetsTask :app:processDebugJavaRes NO-SOURCETask :app:checkDebugDuplicateClassesTask :app:dexBuilderDebugTask :app:mergeLibDexDebugTask :app:mergeDebugJavaResourceTask :app:mergeDebugJniLibFoldersTask :app:validateSigningDebugTask :app:mergeProjectDexDebugTask :app:mergeDebugNativeLibsTask :app:stripDebugDebugSymbolsTask :app:desugarDebugFileDependenciesTask :app:mergeExtDexDebugTask :app:packageDebugTask :app:assembleDebug


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


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


com.android.build.gradle.internal.tasks


下面,我们看看 assembleDebug 打包流程中所需的各个 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 的实现来进行详细分析。

七、重要 Task 实现源码分析

1、资源处理相关 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 过程,看看具体的合并是怎样的。其主体步骤如下所示:

1、获取主 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);

2、执行 Manifest 中的系统属性注入:将主 Manifest 中定义的某些属性替换成 gradle 中定义的属性,例如 package, version_code, version_name, min_sdk_versin 、target_sdk_version、max_sdk_version 等等。

// perform system property injectionperformSystemPropertiesInjection(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);}}}

3、合并 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);}// 1、检查 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())) {// 2、如果 package 定义重复的话,会输出下面信息 String message = mMergeType == MergeType.APPLICATION? String.format("Overlay manifest:package attribute declared at %1s)\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 %1s)\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();}...}

4、合并库中的 manifest 文件

for (LoadedManifestInfo libraryDocument : loadedLibraryDocuments) {mLogger.verbose("Merging library manifest " + libraryDocument.getLocation());xmlDocumentOptional = merge(xmlDocumentOptional, libraryDocument, mergingReportBuilder);if (!xmlDocumentOptional.isPresent()) {return mergingReportBuilder.build();}}

5、执行 manifest 文件中的 placeholder 替换

performPlaceHolderSubstitution(loadedMainManifestInfo,xmlDocumentOptional.get(),mergingReportBuilder,severity);

6、之后对最终合并后的 manifest 中的一些属性进行一次替换,与步骤 2 类似。
7、保存 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

1、首先查看 isIncremental 方法。

// 说明 MergeResources Task 支持增量,肯定重写了 doIncrementalTaskAction 方法 protected boolean isIncremental() {return true;}

2、然后,查看 doFullTaskAction 方法,内部通过 getConfiguredResourceSets 方法获取了 resourceSets,包括了自己的 res 和依赖库的 res 资源以及 build/generated/res/rs。

List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor);

3、创建 ResourceMerger,并使用 resourceSets 进行填充。

ResourceMerger merger = new ResourceMerger(minSdk.get());

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
深入学习-Gradle-自动化构建技术(五)Gradle-插件架构实现原理剖析-