1.apk打包
Android开发中打包apk主要有两种方式:
①使用Android Studio集成直接生成apk;
②使用ant工具在命令行方式下打包apk。
不管哪种方式,打包apk的本质过程都是一样的。
Android的apk包文件包括两部分:代码和资源。所以打包也分为资源打包和代码打包两个方面。
apk打包的流程图:

①aapt打包资源文件,生成R.java文件
②aidl处理aidl文件,生成相应的Java文件
③javac编译源代码+R+aidl,生成class文件
④DX将所有的class文件转换成dex文件
⑤ApkBuilder将资源文件、dex文件打包成apk
⑥KeyStore对apk文件进行签名
⑦ZipAlig对签名后的apk文件进行对齐处理
2.打包流程
①通过aapt工具打包资源文件,生成R.java文件
资源文件主要包括AndroidManifest.xml、布局文件、各种xml资源等。这些资源文件都会被编译,生成相应的R.java文件。(注意:assets和res/raw两个目录里的文件不会在这一步打包,它们都会在打包apk的时候直接打包到apk中,携带在应用里面供应用访问,而且不会被编译成二进制)
打包资源的工具是aapt,全称是Android Asset Packaging Tool。aapt有一个可执行的aapt.exe在Android SDK的build-tools下,选择要编译的SDK版本,如23.0.1,在该目录下命令行里运行指令:
aapt.exe package -f -m -J E:\MyApp\bin (产物R.java输出的目录)
-S MyApp\app\src\main\res (资源文件夹路径)
-I D:\Tools\AndroidSDK\platforms\android-23\android.jar (本地android.jar路径)
-M E:\MyApp\app\src\main\AndroidManifest.xml (项目AndroidManifest.xml)
aapt 参数的含义:
-f 如果编译出来的文件已经存在,强制覆盖。
-m 使生成的包的目录放在-J参数指定的目录。
-J 指定生成的R.java的输出目录
-S res文件夹路径
-A assert文件夹的路径
-M AndroidManifest.xml的路径
-I 某个版本平台的android.jar的路径
-F 具体指定apk文件的输出
②通过aidl工具处理aidl文件,生成相应的Java文件
这一过程使用的工具是aidl(Android Interface Definition Language),即Android接口描述语言。
AIDL的可执行文件也在Android SDK 的build-tools下,aidl.exe。aidl工具解析接口定义文件然后生成相应的Java代码接口供程序调用。
aidl.exe -IE:\MyApp\app\src (项目路径)
-oE:\MyApp\bin (.java文件输出)
E:\MyApp\app\src\main\aidl\com\mytemplateapp\IMyAidlInterface.aidl (需要编译的AIDL文件)
如果在项目没有使用到aidl文件,则可以跳过这一步。
③通过javac工具编译项目源代码,生成class文件
把R.java跟AIDL的java文件编译出来以后,就可以跟其他java源文件一起编译成class文件了。
项目中所有的Java代码,包括R.java和.aidl文件,都会被Java编译器(javac)编译成.class文件,生成的class文件位于工程中的bin/classes目录下。
javac -encoding UTF-8
-bootclasspath D:\Tools\AndroidSDK\platforms\android-23\android.jar
-d E:\MyApp\bin (产物输出路径)
E:\MyApp\app\src\main\java\com\mytemplateapp\*.java
E:\MyApp\bin\com\mytemplateapp\R.java
E:\MyApp\bin\com\mytemplateapp\IMyAidlInterface.java
④通过DX工具将编译好的class文件转换成dex文件
dx工具生成可供Android系统Dalvik虚拟机执行的classes.dex文件,该工具位于android-sdk/platform-tools 目录下。
任何第三方的libraries和.class文件都会被转换成.dex文件。
该过程主要完成将Java字节码转成Dalvik字节码、压缩常量池、消除冗余信息等工作。
dx.bat --dex --output=E:\MyApp\bin\classes.dex E:\MyApp\bin
此处选择class文件的路径需要注意,class文件绝对路径在“E:MyAppbin commytemplateapp”,但是在命令行输入时应该输入到“E:MyAppbin ”,这样编译时className才会跟path对应上。(commytemplateapp 是包名)
⑤通过ApkBuilder工具将资源文件、dex文件打包生成APK文件
所有没有编译的资源(如images等)、编译过的资源和.dex文件都会被apkbuilder工具打包到最终的.apk文件中。
打包的工具apkbuilder位于android-sdk/tools目录下。apkbuilder为一个脚本文件,实际调用的是android-sdk/tools/lib/sdklib.jar文件中的com.android.sdklib.build.ApkbuilderMain类。
⑥使用KeyStore对APK文件进行签名
一旦APK文件生成,它必须被签名才能被安装在设备上。在开发过程中,主要用到的就是两种签名的keystore。一种是用于调试的debug.keystore,在Android Studio中直接run以后跑在手机上的就是使用的debug.keystore。另一种就是用于发布正式版本的keystore。
用SDK自带的一个debug key进行签名:
jarsigner -verbose -keystore C:\Users\Administrator\.android\debug.keystore
-storepass android
-keypass android
E:\MyApp\bin\test_unsigned.apk
androiddebugkey
⑦通过ZipAlig工具对签名后的APK文件进行对齐处理
如果发布的apk是正式版的话,就必须对APK进行对齐处理,用到的工具是zipalign,它位于android-sdk/tools目录下。
对齐的过程是将APK文件中所有的资源文件距离文件的起始距离都偏移为4字节的整数倍,这样通过内存映射访问apk文件时速度会更快。对齐的作用就是减少运行时内存的使用。
zipalign -f 4 ./testbuild/out/test_unsigned.apk ./testbuild/out/test_signed.apk
到此,apk就打包成功了,可以通过ADB安装到手机上了。
3.资源的编译和打包
Android应用程序的设计是代码与资源相分离的,Android的资源文件可以分为两大类:
①assets
assets资源放在主工程assets目录下,它里面保存一些原始的文件,可以以任何方式来进行组织,这些文件最终会原封不动的地被打包进APK文件中。
获取asset资源十分简单:
InputStream is getAssets.open("fileName");
②res
res资源放在主工程的res目录下,这类资源一般都会在编译阶段生成一个资源ID。
res资源包含了开发中使用的各种资源,具体有:animator、anim、color、drawable、layout、menu、raw、values、Xml。这9种类型的资源文件,除了raw类型资源以及Bitmap文件的drawable类型资源之外,其它的资源文件均为文本格式的XML文件,它们在打包的过程中,会被编译成二进制格式的XML文件。这些二进制格式的XML文件分别有一个字符串资源池,用来保存文件中引用到的每一个字符串,包括XL元素标签、属性名称、属性值,以及其它的一切文本值所使用到的字符串。这样原来在文本格式的XML文件中的每一个放置字符串的地方在二进制格式的XML文件中都被替换成一个索引到字符串资源池的整数值,这写整数值统一保存在R.javai类中,R.java会和其他源文件一起编译到APK中去。
前面提到xml编写的Android资源文件都会编译成二进制格式的xm文件,资源的打包都是由AAPT工具来完成的,资源打包主要有以下流程:
①解析AndroidManifest.xml,获得应用程序的包名称,创建资源表。
②添加被引用资源包,被添加的资源会以一种资源ID的方式定义在R.javat中。
③资源打包工具创建一个AaptAssets对象,收集当前需要编译的资源文件,收集到的资源保存在AaptAssets对象对象中。
④将上一步AaptAssets对象保存的资源,添加到资源表ResourceTable中去,用于最终生成资源描述文件resources.arsC。
⑤编译values类资源,这类资源包括数组、颜色、尺寸、字符串等值。
⑥给bag、style、.arrayi这类资源分配资源ID。
⑦编译xml资源文件,编译的流程分为四步:解析xm文件;赋予属性名称资源D;解析属性值:将xm文件从文本格式转换为二进制格式。
⑧生成资源索引表resources.arsc。
4.资源ID
每个Android项目里都有有一个R.java文件:
public final class R {
public static final class anim {
public static final int abc_fade_in=0x7f010000;
}
public static final class attr {
public static final int actionBarDivider=0x7f020000;
}
public static final class String {
public static final int actionBarDivider=0x7f020000;
}
}
每个资源项后的整数就是资源D,资源D是一个4字节的无符整数。
最高字节是Package ID表示命名空间,标明资源的来源,Android系统自己定义了两个Package ID,系统资源命名空间:0x01和应用资源命名空间:0x7f。
次字节是Type ID,表示资源的类型,例如:anim、color、string等。
最低两个字节是Entry ID,表示资源在其所属资源类型中所出现的次序。
资源索引上面提到,最终生成的是资源索引表resources.arsc,Android正是利用这个索引表根据资源D进行资源的查找,为不同语言、不同地区、不同设备提供相对应的最佳资源。查找和通过Resources和AssetManger来完成的。
resources.arsc索引表从上至下文件格式依次为:
文件头:数据结构用ResTable_header来描述,用来描述整个文件的信息,包括文件头大小,文件大小,资源包Package的数量等信息。
全局字符串池:存放所有的字符串,所以资源复用这些字符串,字符串里存放的是资源文件的路径名和资源值等信息。
资源包:会有多个(例如:系统资源包、应用资源包)。
资源包也被划分为以下几个部分:
包头:描述资原包相关信息。
资源类型字符串池:存放资源的类型。
资源名称字符串池:存放资源的名称。
配置列表:存放语音、位置等手机配置信息,用来作为查找资源的标准。
从这里可以看到resources.arsc索引表存在很多常量池,常量池的使用目的也很明显,就是提供资源的复用率,减少resources.arsc索引表的体积,提高索引效率。
assets和res/raw的区别在于:
①assets中的文件资源不会映射到R文件中,而res中的文件会映射到R文件中,所以raw文件夹下的资源都有对应的ID;
②assets可能有更深的目录结构,而res/raw里只能有一层目录;
③资源存取方式不同,assets中利用AssetsManager,而res/raw直接利用getResources()。
5.注意
①除了assets和res/raw资源被原装不动地打包进APK之外,其它的资源都会被编译或者处理;
②除了assets资源外,其它的资源都会被赋予一个资源ID;
③打包工具负责编译和打包资源,编译完成后会生成一个resources.arsc文件和一个R.java,前者保存的是一个资源索引表,后者定义了各个资源D常量。
④应用程序配置文件AndroidManifest.xml同样会被编译成二进制的XML文件,然后再打包到APK里面去。
⑤应用程序在运行时通过AssetManager来访问资源,或通过资源D来访问,或通过文件名来访问。
本文详细介绍了Android APK的打包过程,包括aapt打包资源文件生成R.java,aidl处理接口,javac编译源码,DX转换为dex,ApkBuilder打包资源和dex,KeyStore签名以及ZipAlign对齐处理。每一步骤都详细阐述了其工具、操作和作用,对于理解APK的生成至关重要。
1383

被折叠的 条评论
为什么被折叠?



