Gradle学习之AGP构建及打包流程
|字数总计:2.9k|阅读时长:12分钟|阅读量:
Android Gradle Plugin 简称AGP 是Android编译链中的核心部分,它在Gradle插件的位置如下

本文主要介绍在打出一个APK时 AGP构建的流程以及打包核心流程
AGP的构建流程
由于AGP版本更新变化较大,比如AGP8.0 Transform API将被移除,具体版本变化可以参考Android Gradle 插件版本说明
本文基于
implementation ‘com.android.tools.build:gradle:4.2.1’
App Plugin
我们在项目中会自动添加一个核心plugin
apply plugin: ‘com.android.application’
这个是构建的关键,之前在自定义插件文章中介绍 plugin对应一个具体实现类, 对应的插件 properties 为 ‘com.android.internal.application’,内部标明的插件实现类如下所示:
implementation-class=com.android.build.gradle.internal.plugins.AppPlugin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class AppPlugin extends AbstractAppPlugin { ...
@Override protected void pluginSpecificApply(@NonNull Project project) { }
...
@Override @NonNull protected Class<? extends AppExtension> getExtensionClass() { return BaseAppModuleExtension.class; }
... }
|
AppPlugin 的父类是 AbstractAppPlugin,AbstractAppPlugin 的父类是 BasePlugin,插件的开始就在 BasePlugin#apply 方法里面代码如下,在打包时会执行到插件的Apply方法
1 2 3 4 5 6 7 8
| @Override public final void apply(@NonNull Project project) { CrashReporting.runAction( () -> { basePluginApply(project); pluginSpecificApply(project); }); }
|
重点进入 pluginSpecificApply中,这个方法主要做了一些重要的前置工作,包括路径、AGP版本等,代码及注释如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| private void basePluginApply(@NonNull Project project) { ... DependencyResolutionChecks.registerDependencyCheck(project, projectOptions);
project.getPluginManager().apply(AndroidBasePlugin.class);
checkPathForErrors(); checkModulesForErrors();
PluginInitializer.initialize(project); RecordingBuildListener buildListener = ProfilerInitializer.init(project, projectOptions); 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);
threadRecorder.record( ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION, project.getPath(), null, this::configureExtension);
threadRecorder.record( ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION, project.getPath(), null, this::createTasks); }
|
主要分为2大类
- 插件检查操作
- 插件初始化及配置操作
核心就在上面3个方法中
- configureProject 配置项目
- configureExtension 配置扩展
- createTasks 创建Task
下面将一一介绍
配置项目
configureProject
plugin的准备工程完成之后,就会执行BasePlugin#configureProject,不过需要注意的是,此配置并不是对应Gradle生命周期配置,而是针对当前Project做一些配置工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| private void configureProject() { Provider<ConstraintHandler.CachedStringBuildService> cachedStringBuildServiceProvider = new ConstraintHandler.CachedStringBuildService.RegistrationAction(project) .execute(); Provider<MavenCoordinatesCacheBuildService> mavenCoordinatesCacheBuildService = new MavenCoordinatesCacheBuildService.RegistrationAction( project, cachedStringBuildServiceProvider) .execute(); new LibraryDependencyCacheBuildService.RegistrationAction(project).execute(); new Aapt2WorkersBuildService.RegistrationAction(project, projectOptions).execute(); new Aapt2DaemonBuildService.RegistrationAction(project).execute(); new SyncIssueReporterImpl.GlobalSyncIssueService.RegistrationAction( project, SyncOptions.getModelQueryMode(projectOptions)) .execute(); Provider<SdkComponentsBuildService> sdkComponentsBuildService = new SdkComponentsBuildService.RegistrationAction( project, projectOptions, project.getProviders() .provider(() -> extension.getCompileSdkVersion()), project.getProviders() .provider(() -> extension.getBuildToolsRevision()), project.getProviders().provider(() -> extension.getNdkVersion()), project.getProviders().provider(() -> extension.getNdkPath())) .execute(); GradlePluginUtils.enforceMinimumVersionsOfPlugins(project, issueReporter); project.getPlugins().apply(JavaBasePlugin.class); dslServices = new DslServicesImpl( projectServices, new DslVariableFactory(syncIssueReporter), sdkComponentsBuildService); MessageReceiverImpl messageReceiver = new MessageReceiverImpl( SyncOptions.getErrorFormatMode(projectOptions), projectServices.getLogger()); createLintClasspathConfiguration(project); }
|
主要是一些准备工作
配置Extension
configureExtension
创建工程会有如下配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| android { compileSdk 32
defaultConfig { applicationId "com.qidian.test" minSdk 21 targetSdk 32 versionCode 1 versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" }
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } debug { minifyEnabled false } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } }
|
configureExtension 的目的就是为了将此类的脚本信息转化成代码可以识别的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| private void configureExtension() { DslServices dslServices = globalScope.getDslServices(); final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs = project.container(BaseVariantOutput.class); variantFactory = createVariantFactory(projectServices, globalScope); variantInputModel = new LegacyVariantInputManager( dslServices, variantFactory.getVariantType(), new SourceSetManager( project, isPackagePublished(), dslServices, new DelayedActionsExecutor())); extension = createExtension( dslServices, globalScope, variantInputModel, buildOutputs, extraModelInfo); globalScope.setExtension(extension); variantManager = new VariantManager<>( globalScope, project, projectServices.getProjectOptions(), extension, variantFactory, variantInputModel, projectServices, threadRecorder); registerModels( registry, globalScope, variantInputModel, extension, extraModelInfo); variantFactory.createDefaultComponents(variantInputModel); }
|
看下上面的创建扩展方法,
BasePlugin#createExtension 是个抽象方法,最终交给了 AppPlugin#createExtension 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| protected AppExtension createExtension( @NonNull DslServices dslServices, @NonNull GlobalScope globalScope, @NonNull DslContainerProvider<DefaultConfig, BuildType, ProductFlavor, SigningConfig> dslContainers, @NonNull NamedDomainObjectContainer<BaseVariantOutput> buildOutputs, @NonNull ExtraModelInfo extraModelInfo) { return project.getExtensions() .create( "android", getExtensionClass(), dslServices, globalScope, buildOutputs, dslContainers.getSourceSetManager(), extraModelInfo, new ApplicationExtensionImpl(dslServices, dslContainers)); }
|
它可以获取到上面提及的 build.gradle 下的 android {} 中的任何信息。
创建Task
createTasks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| private void createTasks() { threadRecorder.record( ExecutionType.TASK_MANAGER_CREATE_TASKS, project.getPath(), null, () -> TaskManager.createTasksBeforeEvaluate( globalScope, variantFactory.getVariantType(), extension.getSourceSets())); project.afterEvaluate( CrashReporting.afterEvaluate( p -> { variantInputModel.getSourceSetManager().runBuildableArtifactsActions(); threadRecorder.record( ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS, project.getPath(), null, this::createAndroidTasks); })); }
|
这个方法里面主要有两个方法:
- TaskManager#createTasksBeforeEvaluate: 静态方法表示在 Project 配置前,会创建一批 Task。
- createAndroidTasks:注册了一个配置生命周期完成后的回调,等到 Project 配置完成后,Variant 已经确定完毕,又会创建一批 Task。
这些task大部分都是为apk打包服务,网上有个比较详细的图

APK 打包流程
从上面我们知道 AGP提供了核心框架,通过不同的Task去组成一个复杂且解耦的打包流程,我们可以通过以下方式看到Task
./gradlew app:assembleDebug –console=plain
或者 AS 右侧 Gradle-Tasks-build-assemble
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| ./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
|
这些Task一起配合完成最终的打包,网上有几个图还不错
官网

Android Studio Project Site 还有个更加细节的

在前一个章节最后,我们可以看到,最后是TaskManager来创建具体的Task,其中,打包流程的大部分tasks都在这个目录中
com.android.build.gradle.internal.tasks
通过下面的表格 看下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,锚点使用 |
| 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,锚点使用 |
这些Task各司其职形成一个有向无环图,完成了apk打包。这些Task主要分为3种类型
- 增量Task
继承于 NewIncrementalTask 这个增量 Task 基类,需要重写 doTaskAction 抽象方法实现增量功能
- 非增量Task
继承于 NonIncrementalTask 这个非增量 Task 基类,重写 doTaskAction 抽象方法实现全量更新功能
- Transform Task
自定义Transfrom在调用appExtension.registerTransform(new CustomTransform()) 注册方法时将其保存到当前的 Extension 类中的 transforms 列表中,当 LibraryTaskManager/TaskManager 调用 createPostCompilationTasks(负责为给定 Variant 创建编译后的 task)方法时,会取出相应 Extension 中的 tranforms 列表进行遍历,并通过 TransformManager.addTransform 方法将每一个 Transform 转换为与之对应的 TransformTask 实例,而该方法内部具体是通过 new TransformTask.CreationAction(…) 的形式进行创建
在4.0以下还有一些transformClassesWithDexBuilderForxxx,打包流程的Task校之4.0也有不同,在 3.0.1中主要Task如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| :app:preBuild UP-TO-DATE → 空task,锚点 :app:preDebugBuild → 空task,锚点 :app:compileDebugAidl NO-SOURCE → 处理AIDL :app:checkDebugManifest → 检查Manifest是否存在 :app:compileDebugRenderscript NO-SOURCE → 处理renderscript :app:generateDebugBuildConfig → 生成 BuildConfig.java :app:mainApkListPersistenceDebug → 生成 app-list.gson :app:generateDebugResValues → 生成resvalue,generated.xml :app:generateDebugResources → 空task,锚点 :app:mergeDebugResources → 合并资源文件 :app:createDebugCompatibleScreenManifests → manifest文件中生成compatible-screens,指定屏幕适配 :app:processDebugManifest → 合并manifest.xml文件 :app:processDebugResources → aapt打包资源 :app:compileDebugKotlin → 编译Kotlin文件 :app:prepareLintJar UP-TO-DATE → 拷贝 lint jar包到指定位置 :app:generateDebugSources → 空task,锚点 :app:javaPreCompileDebug → 生成 annotationProcessors.json 文件 :app:compileDebugJavaWithJavac → 编译 java文件 :app:compileDebugNdk → 编译ndk :app:compileDebugSources → 空task,锚点 :app:mergeDebugShaders → 合并 shader文件 :app:compileDebugShaders → 编译 shaders :app:generateDebugAssets → 空task,锚点 :app:mergeDebugAssets → 合并 assests文件 :app:validateSigningDebug → 验证签名 :app:signingConfigWriterDebug → 编写SigningConfig信息 :app:checkDebugDuplicateClasses → 检查重复class :app:transformClassesWithDexBuilderForDebug → class打包成dex :app:transformDexArchiveWithExternalLibsDexMergerForDebug → 打包第三方库的dex :app:transformDexArchiveWithDexMergerForDebug → 打包最终的dex :app:mergeDebugJniLibFolders → 合并jni lib 文件 :app:transformNativeLibsWithMergeJniLibsForDebug → 合并jnilibs :app:transformNativeLibsWithStripDebugSymbolForDebug → 去掉native lib里的debug符号 :app:processDebugJavaRes NO-SOURCE → 处理java res :app:transformResourcesWithMergeJavaResForDebug → 合并java res :app:packageDebug → 打包apk :app:assembleDebug → 空task,锚点 :app:extractProguardFiles → 生成混淆文件
# 还会打一个release包,task和上述基本一致,此处省略~
|
参考
- Android Gradle 插件版本说明
- 总听说AGP,它到底做了什么?
- Gradle 插件架构实现原理剖析 — 下
- Android Gradle Plugin 主要流程分析
- Android Gradle Plugin 主要 Task 分析
- 从AGP构建过程到APK打包过程