构建与打包能力(四、APK打包原理剖析)

612 阅读3分钟

构建与打包能力系列

1、aapt2 命令行实现apk打包

apk文件结构

  • classes.dex:Dex是DalvikVM executes的缩写,即Android Dalvik执行文件
  • AndroidManifest.xml:Project中AndroidManifest.xml编译后得到的二进制xml文件
  • META-INF:主要保存各个资源文件的SHA1 hash值,用于校验资源文件是否被篡改,防止二次打包时资源文件被替换,该目录下主要包括下面三个文件:
    • MANIFEST.MF:保存版本号以及对每个文件(包括资源文件)整体的SHA1 hash
    • CERT.SF:保存对每个文件头3行的SHA1 hash
    • CERT.RSA:保存签名和公钥证书
  • res:Project中res目录下资源文件编译后得到的二进制xml文件
  • resources.arsc:包含了所有资源文件的映射,可以理解为资源索引,通过该文件能找到对应的资源文件信息

aapt2打包流程

老的版本 image.png

新的版本

image.png

(1)通过aapt2打包res资源文件:生成R.java、resource.arsc和res文件(二进制&非二进制如res/raw和pic保持原样)

(2)通过Javac编译R.java、Java接口文件、Java源文件:生成.class文件

(3)通过d8命令:将.class文件和第三方库中的.class文件处理生成classes.dex

(4)通过aapt2工具:将aapt生成的resources.arsc和res文件、未编译的资源assets文件和classes.dex一起打包生成apk

(5)通过zipalign工具:将未签名的apk进行对齐处理

(6)通过apksigner工具:对上面的apk进行debug或release签名

image.png

aapt2命令行实现打包

  • 使用aapt2 compile编译资源文件,把*.xml文件压缩成二进制文件使之体积更小,运行时解析更快。drawable文件默认会被压缩,raw/目录下文件原封不动
    • --dir 资源文件的目录
    • -o 编译后的资源输出位置
    //对单个文件编译
    aapt2 compile app/src/main/res/values/strings.xml -o apk/
    //对目录下的所有资源打包编译
    aapt2 compile --dir app/src/main/res/ -o apk/res.zip
    
  • 使用 aapt2 link 为资源分配id,并生成R.java文件和resources.arsc资源索引表文件
    • -I 参与资源链接的android.jar的路径
    • --java 指定生成的R.java文件的路径
    • --manifest 指定参与资源链接的manifest文件的路径
    • -o 指定资源链接后输出的.apk的路径(此时不包含.dex文件,也没签名)
     aapt2 link apk/res.zip 
     -I android_sdk/platforms/android_version/android.jar 
     --java apk/ 
     --manifest app/src/main/AndroidManifest.xml 
     -o apk/res.apk
    
  • 使用javac编译.java文件,生成.class文件
    • -encoding 指定编译的格式
    • -target 指定参与编译的jdk版本
    • -bootclasspath 指定参与编译的android.jar的路径
    • -d 指定输出的.class文件的路目录
    javac -encoding utf-8
    -target 1.8
    -bootclasspath android_sdk/platforms/android_version/android.jar 
    app/src/main/java/org/example/aapt/*.java apk/org/example/aapt/R.java 
    -d apk
    
  • 使用d8命令把.class文件转成.dex文件
    • 输入为aapt目录下所有.class文件
    • --lib指定参与编译的android.jar的路径
    • --classpath指定参与编译的类路径资源(appcompat,recyclerview)
    • --output指定输出的.dex文件的路径,这里我们需要把它指定到项目根目录下,下一步需要把dex和res合并,否则会把dex文件的文件夹也带进去
    d8 apk/org/example/aapt/*.class 
    --lib android_sdk/platforms/android_version/android.jar 
    --output ./
    
  • 使用aapt把classes.dex添加到res.apk里面去
    • 原来这步是通过apkbuilder工具,现在改成用aapt命令来做。
    aapt add apk/res.apk classes.dex
    
  • 使用zipalign工具对.apk字节码进行4字节对齐
zipalign 4 apk/res.apk apk/app-unsigned-aligned.apk
  • 使用apksigner对.apk进行签名
    • --ks key密钥文件路径
    • --out 签名后的.apk文件路径
    • 最后参与签名的.apk文件的路径
apksigner sign --ks key --out apk/app-release.apk apk/app-unsigned-aligned.apk

gradle构建工具打包

assembleDebug打包流程中关键的Task所对应的实现类与含义

image.png