在Android Studio中直接点击 Run ‘app’ 就能够在build/outputs/apk生成能够在android设备中安装的Apk文件,那么Apk生成的过程是怎么样的呢?
html
Apk文件大概能够分为两个部分:代码和资源,因此打包的也分为代码和资源两个部分,咱们能够根据Google提供的流程图来具体了解一个Apk的构建过程java
新版构建流程图android
APK打包的内容主要有:应用模块也就是本身开发的用到的源代码、资源文件、aidl接口文件,还有就是依赖模块即源代码用到的第三方依赖库如:aar、jar、so文件
数组
为了可以清楚的了解Apk是如何生成的, 来看一下老版构建流程图安全
老版构建流程图 bash
在了解Apk生成的过程以前,咱们须要了解一下图中各个工具的做用app
名字 | 功能 |
---|---|
AAPT/APT2 | Android资源打包工具 |
AIDL | 将全部的AIDL接口转化为java接口 |
Javac(Java Compiler) | 将全部的Java代码编译成Class文件 |
Dex | 将Class文件编译成Dex文件 |
Apkbuilder | 将处理后的资源和代码打包生成Apk文件 |
Jarsigner/Apksigner | 对未签名的apk文件进行签名 |
Zipalign | 优化签名后的Apk,减小运行时所占用的内存 |
AAPT(Android Asset Packaging Tool)android资源打包工具,将资源文件(包括AndroidManifest.xml、布局文件、各类xml资源等)打包生成R.java文件,将AndroidManifest.xml生成二进制的AndroidManifest.java文件
工具
aapt p -M AndroidManifest.xml -S output/res/ -I android.jar -J ./ -F input/out.apk
p:打包
-M:AndroidManifest.xml文件路径
-S:res目录路径
-A:assets目录路径
-I:android.jar路径,会用到的一些系统库
-J 指定生成的R.java的输出目录
-F 具体指定apk文件的输出
复制代码
可是从从Android Studio 3.0开始,google默认开启了AAPT2做为资源编译的编译器,AAPT2的出现,为资源的增量编译提供了支持,aapt2 主要分两步,compile 和 link
布局
compile优化
aapt2 compile -o res.apk --dir output/res/
-o:指定已编译资源的输出路径
--dir:指定包含多个资源文件的资源目录
复制代码
link
aapt2 link -o input/out.apk -I tools/android.jar --manifest output/AndroidManifest.xml -A res.apk --java ./
-o:指定连接的资源 APK 的输出路径
-I:指定android.jar路径
--manifest:指定AndroidManifest.xml路径
--java :指定要在其中生成 R.java 的目录
复制代码
使用AIDL(Android Interface Denifition Language),位于sdk\build-tools目录下的aidl工具,将源码文件、aidl文件、framework.aidl等全部的AIDL文件,生成相应的Java文件,命令以下:
aidl -Iaidl -pAndroid/Sdk/platforms/android-29/framework.aidl -obuild aidl/com/android/vending/billing/IInAppBillingService.aidl
-I 指定import语句的搜索路径,注意-I与目录之间必定不要有空格
-p 指定系统类的import语句路径,若是是要用到android.os.Bundle系统的类,必定要设置sdk的framework.aidl 路径
-o 生成java文件的目录,注意-o与目录之间必定不要有空格,并且这设置项必定要在aidl文件路径以前设置
复制代码
使用Javac(Java Compiler)把项目中全部的Java代码编译成class文件, 包括Java源文件、AAPT生成的R.java文件 以及 aidl生成的Java接口文件,命令以下:
javac -target 1.8 -bootclasspath platforms/android-28/android.jar -d ./java/com/testjni/*.java
复制代码
使用DX工具将全部的Class文件(包括第三方库中的class文件)转换成Dex文件(Dalvik 可执行文件,其中包括在 Android 设备上运行的字节码),该过程主要完成Java字节码转换成Dalvik字节码, 命令以下:
java -jar dx.jar --dex --ouput=classes.dex ./java/com/testjni/*.class
--dex:将class文件转成dex文件
--output:指定生成dex文件到具体位置
复制代码
使用Apkbuilder(主要用到的是sdk/tools/lib/sdklib.jar文件中的ApkBuilderMain类)将全部的Dex文件、Resource.arsc、Res文件夹、Assets文件夹、AndroidManifest.xml 打包生成Apk文件(未签名)
使用Apksigner(Android官方针对apk签名及验证工具)或 Jarsigner(JDK提供针对jar包签名工具)对未签名的apk文件进行签名
ps:若是使用Apksigner签名须要(7. 优化Apk文件)放到(6. 对Apk文件签名)签名前面,为何?请查看关于Apksigner 和 Jarsigner的区别,请移步到文末
使用zipalign对签名后的apk文件进行对齐处理,对齐的主要过程是将APK包中全部的资源文件距离文件起始偏移为4字节整数倍,这样经过内存映射访问apk文件时的速度会更快,减小其在设备上运行时所占用的内存
上述打包过程都是AndroidStudio编译时,调用各类编译命令自动完成的, 总结一下上述打包过程:
Apk文件大概能够分为两个部分:代码和资源, 代码部分经过Javac将Java代码编译成Class文件, 而后经过DX工具将Class文件编译成Dex文件,接下来咱们主要来分析一下资源的编译和打包
在分析资源的编译和打包以前,咱们须要了解一下Android都有哪些资源,其实Android资源大概分为两个部分:assets 和 res
assets资源放在assets目录下,它里面保存一些原始的文件,能够以任何方式来进行组织,这些文件最终会原封不动的被打包进APK文件中,经过AssetManager来获取asset资源,代码以下
AssetManager assetManager = context.getAssets();
InputStream is = assetManager.open("fileName");
复制代码
res资源放在主工程的res目录下,这类资源通常都会在编译阶段生成一个资源ID供咱们使用,res目录包括animator、anim、 color、drawable、layout、menu、raw、values、xml等
上述资源文件除了raw类型资源,以及drawable文件夹下的Bitmap资源以外,其它的资源文件均会被编译成二进制格式的XML文件,生成的二进制格式的XML文件分别有一个字符串资源池,用来保存文件中引用到的每个字符串
这样原来在文本格式的XML文件中的每个放置字符串的地方在二进制格式的XML文件中都被替换成一个索引到字符串资源池的整数值,将整数值保存在R.java类中,R.java会和其余源文件一块儿编译到APK中去
将资源编译成二进制文件,都是由AAPT工具来完成的,资源打包主要有如下几个流程:
AAP工具会全部的资源都会生成一个R.java文件,而且每一个资源都对应R.java中的十六进制整数变量,其实这些十六进制的整数是由三部分组成:PackageId + TypeId + ItemValue,代码所示:
public final class R {
public static final class anim {
public static final int abc_fade_in=0x7f010000;
public static final int abc_fade_in=0x7f010001;
//***
}
public static final class string {
public static final int a11y_no_data=0x7f100000;
public static final int a11y_no_permission=0x7f100001;
//***
}
}
复制代码
最高字节是Package ID表示命名空间,标明资源的来源,Android系统本身定义了两个Package ID,系统资源命名空间:0x01 和 应用资源命名空间:0x7f
正由于应用资源命名空间:0x7f,咱们在作插件化的时候就会出现一个问题,宿主和插件包,合并资源后资源id冲突。经过上面分析要解决这个问题,就要为不一样的插件设置不一样的PackageId,而宿主能够保留原来0x7f不变,这样就永远不会有冲突发生了
最终生成的是资源索引表resources.arsc,resources.arsc是一个编译后的二进制文件, 在AndroidStudio打开resources.arsc文件,以下所示
Android正是利用这个索引表根据资源ID进行资源的查找,为不一样语言、不一样地区、不一样设备提供相对应的最佳资源。查找和经过Resources和 AssetManger来完成的
在文中提到了两个工具Apksigner 和 Jarsigner,下面一块儿来了解一下Apksigner 和 Jarsigner的区别
在Android Studio中点击菜单 Build->Generate signed apk... 打包签名过程当中,能够看到两种签名选项 V1(Jar Signature) V2(Full APK Signature)
从Android 7.0开始, 谷歌增长新签名方案 V2 Scheme (APK Signature),但Android 7.0如下版本, 只能用旧签名方案 V1 scheme (JAR signing)
来自JDK(Jarsigner),对zip压缩包的每一个文件进行验证, 签名后还能对压缩包修改(移动/从新压缩文件),对V1签名的apk/jar解压,在META-INF存放签名文件(MANIFEST.MF, CERT.SF, CERT.RSA), 其中MANIFEST.MF文件保存全部文件的SHA1指纹(除了META-INF文件), 由此可知: V1签名是对压缩包中单个文件签名验证
来自Google(apksigner), 对zip压缩包的整个文件验证, 签名后不能修改压缩包(包括zipalign), 对V2签名的apk解压, 没有发现签名文件, 从新压缩后V2签名就失效, 由此可知: V2签名是对整个APK签名验证
建立发布密钥库,请参阅在 Android Studio 中为应用签名
注意: apksigner工具默认同时使用V1和V2签名,以兼容Android 7.0如下版本