
如图,
编译器将源代码(包括 Application Module 及其所依赖的所有 Library 源代码)转换成DEX(Dalvik Executable)文件(其中包括运行在 Android 设备上的字节码),将所有其他内容转换成已编译资源。(- 将源代码(包括 Application Module 和 Library Module)编译成 class 文件,再将所有的 class 文件(包括第三方库中)打包生成 dex 文件。
- 解压所有 aar 包中的资源文件,并和项目中所有资源文件合并到一个目录。
- 生成资源文件的索引文件。
APK 打包器将 DEX 文件和已编译资源打包生成单个 APK。不过,必须先对 APK 签名,才能将应用安装并部署到 Android 设备上。APK 打包器使用 Debug 或 Release 密钥库对 APK 签名:- 如果您构建的是 Debug 版本的应用(即专用于测试和分析的应用),
打包器会使用 Debug 密钥库签名应用。Android Studio 自动使用 Debug 密钥库配置新项目。 - 如果您构建的是打算向外发布的 Release 版本应用,
打包器会使用 Release 密钥库签名应用。
- 如果您构建的是 Debug 版本的应用(即专用于测试和分析的应用),
- 在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,减少其在设备上运行时的内存占用。
2. Gradle 构建 tasks 说明
注:这里是用 Debug 模式做例子,Release 模式时只需将 task 中的 Debug 替换成 Release 理解即可)Executing tasks: [:app:assembleDebug] :app:preBuild UP-TO-DATE :app:preDebugBuild UP-TO-DATE :app:compileDebugAidl UP-TO-DATE :app:compileDebugRenderscript UP-TO-DATE :app:generateDebugResValues UP-TO-DATE :app:generateDebugResources UP-TO-DATE :app:mergeDebugResources UP-TO-DATE :app:transformDataBindingBaseClassLogWithDataBindingMergeGenClassesForDebug UP-TO-DATE :app:dataBindingGenBaseClassesDebug UP-TO-DATE :app:checkDebugManifest UP-TO-DATE :app:generateDebugBuildConfig UP-TO-DATE :app:prepareLintJar UP-TO-DATE :app:mainApkListPersistenceDebug UP-TO-DATE :app:createDebugCompatibleScreenManifests UP-TO-DATE :app:processDebugManifest :app:splitsDiscoveryTaskDebug UP-TO-DATE :app:processDebugResources :app:generateDebugSources :app:dataBindingExportBuildInfoDebug :app:javaPreCompileDebug UP-TO-DATE :app:transformDataBindingWithDataBindingMergeArtifactsForDebug UP-TO-DATE :app:compileDebugJavaWithJavac :app:compileDebugNdk NO-SOURCE :app:compileDebugSources :app:mergeDebugShaders UP-TO-DATE :app:compileDebugShaders UP-TO-DATE :app:generateDebugAssets UP-TO-DATE :app:mergeDebugAssets UP-TO-DATE :app:transformClassesWithDexBuilderForDebug :app:transformDexArchiveWithExternalLibsDexMergerForDebug :app:transformDexArchiveWithDexMergerForDebug :app:mergeDebugJniLibFolders UP-TO-DATE :app:transformNativeLibsWithMergeJniLibsForDebug UP-TO-DATE :app:processDebugJavaRes NO-SOURCE :app:transformResourcesWithMergeJavaResForDebug UP-TO-DATE :app:validateSigningDebug UP-TO-DATE :app:packageDebug :app:assembleDebug BUILD SUCCESSFUL in 20s
特别说明:由于 Gradle 会尝试通过不重复执行输入未发生变化的 task 来节省时间(这些 task 被标记为 UP-TO-DATE,如上所示)
所以第一次编译运行会比较久,而后面就不会了。
上面列出来的 Gradle 构建过程,大致可分为五个阶段:
Preparation of dependecies在这个阶段 Gradle 检测 Module 依赖的所有 library 是否 ready。如果这个 Module 依赖于另一个 Module ,则另一个 Module 也要被编译。Merging resources and processing Manifest打包资源和 Manifest 文件。Compiling在这个阶段处理编译器的注解,源码被编译成字节码。Postprocessing所有带 transform 前缀的 task 都是这个阶段进行处理的。Packaging and publishing这个阶段 library 生成.aar文件,Application 生成.apk文件。
这五个阶段和上面的构建过程中 task 的时序有大致的对应关系,大家可以相互映照着理解。
为了便于大家理解,下面给出一些关键 task 的说明:
-
mergeDebugResources解压所有的 aar 包输出到app/build/intermediates/exploded-aar,并且把所有的资源文件合并到app/build/intermediates/res/merged/debug目录里。 -
processDebugManifest把所有 aar 包里的AndroidManifest.xml中的节点,合并到项目的AndroidManifest.xml中,并根据app/build.gradle中当前buildType 的 manifestPlaceholders配置内容替换 manifest 文件中的占位符,最后输出到app/build/intermediates/manifests/full/debug/AndroidManifest.xml。 -
processDebugResources- 调用 aapt 生成项目和所有 aar 依赖的
R.java,输出到app/build/generated/source/r/debug目录; - 生成资源索引文件
app/build/intermediates/res/debug/resources-debug.ap_; - 把符号表输出到
app/build/intermediates/symbols/debug/R.txt。
- 调用 aapt 生成项目和所有 aar 依赖的
-
compileDebugJavaWithJavac用来把 java 文件编译成 class 文件,输出的路径是app/build/intermediates/classes/debug编译的输入目录有
- 项目源码目录,默认路径是
app/src/main/java,可以通过 sourceSets 的 dsl 配置,允许有多个(打印project.android.sourceSets.main.java.srcDirs可以查看当前所有的源码路径,具体配置可以参考 android-doc,以及这里。 app/build/generated/source/aidl。app/build/generated/source/buildConfig。app/build/generated/source/apt(继承javax.annotation.processing.AbstractProcessor做动态代码生成的一些库,输出在这个目录)的代码。
- 项目源码目录,默认路径是
-
transformClassesWithMultidexlistForDebug(这个任务在使用 Multidex 才会出现)它有两个作用- 扫描项目的
AndroidManifest.xml文件和分析类之间的依赖关系,计算出那些类必须放在第一个 dex 里面,最后把分析的结果写到app/build/intermediates/multi-dex/debug/maindexlist.txt文件里面 - 生成混淆配置项输出到
app/build/intermediates/multi-dex/debug/manifest_keep.txt文件里
项目里的代码入口是 manifest 中 application 节点的属性 android.name 配置的继承自 Application 的类,在 Android 5.0 以前的版本系统只会加载一个 dex(classes.dex),classes2.dex .......classesN.dex 一般是使用
android.support.multidex.MultiDex加载的,所以如果入口的 Application 类不在 classes.dex 里 5.0 以下肯定会挂掉,另外当入口 Application 依赖的类不在 classes.dex 时初始化的时候也会因为类找不到而挂掉,还有如果混淆的时候类名变掉了也会因为对应不了而挂掉,综上所述就是这个任务的作用。 - 扫描项目的