Android系统:打包,安装,运行:构建系统

一般打包系统包括,编译,处理依赖等。在Android大型工程中,通常代码量很庞大,业务模块众多,会因为打包时间的漫长,拖沓调试,开发的节奏,这里介绍Android应用的构建流水线,包括使用SDK工具,和Android Gradle Plugin(AGP),更好理解Android构建系统。

1. 构建APK

以下面项目结构为基础,介绍构建流程:

# demo项目的文件结构
.
├── build.sh
├── debug.keystore
└── src
    ├── main
    │   ├── AndroidManifest.xml
    │   ├── assets
    │   │   └── test.txt
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── myapplication
    │   │               └── MainActivity.java
    │   └── res
    │       ├── layout
    │       │   └── activity_main.xml
    │       ├── mipmap-hdpi
    │       │   └── ic_launcher.png
    │       └── values
    │           └── strings.xml

APK的编译一般遵循下面的流程:

顺序 内容 产物
1 aapt 编译资源文件 R.java,resources.arsc映射文件,.flat(编译后的资源文件),apk壳文件
2 javac 编译R文件,编译源代码,生成APT .class文件
3 转换.class字节码目标文件成.dex文件 .dex文件
4 .dex,资源文件压解到apk中 .apk文件
5 将apk压缩文件进行对齐操作 apk algined
6 apksigner签名加密apk apk signed
1.1 Native pipline

使用Android sdk build-tools中的,dx(d8),aapt,一系列工具构建一个完整的apk文件,由demo中的 build.sh实现,它遵循了上面介绍的打包流程,下面简短显示 build.sh 的几个步骤。

# 1. AAPT2 compile...
$AAPT2 compile --no-crunch -o $FLATS ${LAYOUT}/activity_main.xml

# 1.2 AAPT2 linking (and create R.java) ...
$AAPT2 link -o $BIN/base.apk --java $GEN_SRC --manifest $GEN_SRC/AndroidManifest.xml -I $PLATFORM $FLATS/*

# 2. Compiling source...
javac -d $OBJ -classpath src -classpath $OBJ -bootclasspath $PLATFORM -source 1.7 -target 1.7 $GEN_SRC/MainActivity.java

# 3 Translating in class to dex ...
$DX --dex --incremental --output=$BIN/classes.dex $OBJ

# 4 Adding dexs to APK...
(cd $BIN; zip -r base.apk classes.dex )

# 5. Create keytore via command
$APKSIGNER sign -v --ks debug.keystore -ks-pass pass:android --min-sdk-version $SDK_VERSION --v1-signing-enabled true --v2-signing-enabled true $BIN/base.apk

1.2 AGP pipline:

官方建议AGP打包,其实是对1.1.1的封装,使用Gradle中Plugin,Extension,Task,Action的架构,和更容易理解的配置语言,实现打包流程(经过大量优化),下面是Gradle实现的流水线,细节略。

$ gradlew assembleDebug (com.android.tools.build:gradle:3.0.0)

:app:preBuild
:app:preDebugBuild
:app:compileDebugAidl
:app:compileDebugRenderscript
:app:checkDebugManifest
:app:generateDebugBuildConfig
:app:prepareLintJar
:app:generateDebugResValues
:app:generateDebugResources
:app:mergeDebugResources
:app:createDebugCompatibleScreenManifests
:app:processDebugManifest
:app:splitsDiscoveryTaskDebug
:app:processDebugResources
:app:generateDebugSources
:app:dataBindingExportBuildInfoDebug
:app:javaPreCompileDebug
:app:transformDataBindingWithDataBindingMergeArtifactsForDebug
:app:compileDebugJavaWithJavac
:app:compileDebugNdk NO-SOURCE
:app:compileDebugSources
:app:mergeDebugShaders
:app:compileDebugShaders
:app:generateDebugAssets
:app:mergeDebugAssets
:app:transformClassesWithDexBuilderForDebug // per-class dexing for sources
:app:transformDexArchiveWithExternalLibsDexMergerForDebug 
// per-class dexing for libs(aar,jar)
// store file into build/intermediates/transforms/externalLibsDexMerger/debug/0/
:app:transformDexArchiveWithDexMergerForDebug
:app:mergeDebugJniLibFolders
:app:transformNativeLibsWithMergeJniLibsForDebug
:app:processDebugJavaRes NO-SOURCE
:app:transformResourcesWithMergeJavaResForDebug
:app:validateSigningDebug
:app:packageDebug
:app:assembleDebug

2 AAR

aar生成过程由javac,aapt组成。为了避免重复编译,aar中依赖的jar不会在自己打包的过程中被编译。

# aar 文件结构
/AndroidManifest.xml
/classes.jar
/res/
/R.txt                  
# R.txt 由该命令生成:aapt link --output-text-symbols [path/]R.txt 设置aar中的资源文件的唯一id
/public.txt (optional)

3 AAB

4 总结

APK文件是运行应用所需的最终产物,大型项目常见的把代码拆分成不同的aar,来解耦工程,在打包效率上看,因为aar包构建完成后,还要再构建一次apk,这样做其实是低效的(重复的构建Task和Action),下面表格显示一个工程,构建aar之后再构建apk和单纯构建这个工程的apk之间的相对时间差别。

Order Action APK AAR APK+AAR Ratio
1 clean
2 assembleDebug 64 19 46 65 1.54%
3 assembleDebug 修改colors.xml 9 6 27 33 72.73%
4 assembleDebug 删除Java文件中一句代码 20 12 25 37 45.95%
5 assembleDebug 删除布局中一个TextView 19 13 35 48 60.42%

参考: