我们都知道,在 Android 项目的开发过程中,只需要点一下 Android Studio
的运行按钮(没错,就是那个三角按钮?),就可以将代码编译好并运行在设备上。相信任何一个 Android 开发者都知道,从我们点击按钮到设备上运行 APP,Android Studio 默默地做了编译、打包(生成 APK 文件)、安装、运行等很多事情,安装和运行不是本文所关心的内容。下面,我们一起来看看,Android Studio 的编译、打包(也可以合并称为构建)过程。
1. 构建流程
先来看下 Android 官网给出的构建流程图:
如图所示,典型 Android 应用模块的构建流程通常依循下列步骤:
-
编译器
将源代码
(包括 Application Module 及其所依赖的所有 Library 源代码)转换成DEX
(Dalvik Executable)文件(其中包括运行在 Android 设备上的字节码),将所有其他内容
转换成已编译资源
。 -
APK 打包器
将 DEX 文件和已编译资源合并成单个 APK。不过,必须先对 APK 签名,才能将应用安装并部署到 Android 设备上。 -
APK 打包器
使用 Debug 或 Release 密钥库对 APK 签名:- 如果您构建的是 Debug 版本的应用(即专用于测试和分析的应用),
打包器
会使用 Debug 密钥库签名应用。Android Studio 自动使用 Debug 密钥库配置新项目。 - 如果您构建的是打算向外发布的 Release 版本应用,
打包器
会使用 Release 密钥库签名应用。
- 如果您构建的是 Debug 版本的应用(即专用于测试和分析的应用),
- 在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,减少其在设备上运行时的内存占用。
构建流程
结束后,得到可用来进行部署、测试的 Debug APK,或者可用来发布给外部用户的 Release APK。
本段内容参考 Android 官网(需科学上网,不能科学上网的朋友点这里)
上面 Android 官方只是给出了 Android 大致的构建流程,并没有详细,下面通过实际构建来看一下,Android Studio 的 build 过程。
2. Gradle 构建 tasks 说明
我们通过 Android Studio 的 build -> Build APK 功能来看下,build 过程中是怎样的。Build APK 会执行以下 Gradle task。 (注:
这里是用 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
文件里
android.support.multidex.MultiDex
加载的,所以如果入口的 Application 类不在 classes.dex 里 5.0 以下肯定会挂掉,另外当入口 Application 依赖的类不在 classes.dex 时初始化的时候也会因为类找不到而挂掉,还有如果混淆的时候类名变掉了也会因为对应不了而挂掉,综上所述就是这个任务的作用。 -
扫描项目的
3. 总结
简而言之,Android Studio 的构建构成总结为以下几点:
- 将源代码(包括 Application Module 和 Library Module)编译成 class 文件,再将所有的 class 文件(包括第三方库中)打包生成 dex 文件。
- 解压所有 aar 包中的资源文件,并和项目中所有资源文件合并到一个目录。
- 生成资源文件的索引文件。
- 最终将所有的dex文件和已编译的资源文件,打包生成
.apk
文件。
到这里,大家再次对照上面的流程图,联合理解,相信会有一种明悟的赶脚?