APK打包过程
前言
HI,欢迎来到裴智飞的《每周一博》。今天是一月第二周,我给大家介绍下APK的打包过程。这篇文章是在上一篇《APK结构介绍》的基础上进行实践,先上一张详细的打包流程图。
详细打包图接下来我们用工具手动打包一个Android项目。
1. 配置环境变量
我们用到的工具在build-tools目录下的不同版本中。
AAPT_HOME=/Users/peizhifei/Library/Android/sdk/build-tools/27.0.3/
export AAPT_HOME
export PATH=$PATH:$AAPT_HOME
创建一个测试项目,然后拷贝/Users/peizhifei/Library/Android/sdk/platforms/android-27/android.jar到项目根目录
2. 创建辅助目录
进入项目下面,生成gen存放R文件,build放过程文件,out放输出文件。
cd app/src/main/
mkdir -p app/src/main/{gen,build,out}
3. 生成R文件
要使用aapt工具,输入Manifest和res和android.jar来生成R文件。
aapt p -f -M ./app/src/main/AndroidManifest.xml
-I android.jar -S ./app/src/main/res/ -J ./app/src/main/gen/ -m
aapt命令参数含义:
-f : #如果编译出来的文件已经存在,强制覆盖
-M : AndroidManifest.xml 的路径
-I : android.jar的路径
-S : 资源文件res 文件夹路径
-J :生成 R.java 的输出目录
-m : 让生成包的目录放在 -J 参数指定的目录
成功后可以再gen文件下会生成相应的R.java文件。
4. 编译aidl文件
通过aidl工具就可以将aidl文件编译成java文件。
aidl -Iapp/src app/src/main/aidl/com/example/peizhifei/myapplication/IMyAidlInterface.aidl
注意-Iapp/src之间是没有空格的。
5. 编译生成class文件
这一步就是用javac将项目中所有的java文件编译成class文件。
javac -source 1.8 -target 1.8 -encoding UTF-8
-bootclasspath android.jar -d app/src/main/build/
app/src/main/java/com/example/peizhifei/myapplication/*.java
app/src/main/gen/com/example/peizhifei/myapplication/*.java
app/src/main/aidl/com/example/peizhifei/myapplication/*.java
命令参数含义如下:
-encoding :编码方式,这里设置UTF-8;
-bootclasspath :引导类文件的路径,这里需要使用到android.jar中的Android API;
-d :生成的class文件存放路径;
这里指定了所有的java文件,包括源码的,aidl编译的java文件,gen里面的R文件,结果存在在build下面。
6. 将class文件生成dex文件
dex文件是Android虚拟机可运行文件,可以使用dx工具生成。
dx --dex --output=app/src/main/build/classes.dex app/src/main/build/
输入是所有的class文件,输出在build下面,如果方法数超过Integer.Max多会出现多个dex文件。
7. 生成资源映射表
资源索引表resources记录了从资源id到文件路径的转换关系,当应用通过R文件使用资源时,会先从resources中拿到文件路径,然后通过AssetManager进行访问。
aapt package -f -M app/src/main/AndroidManifest.xml
-I android.jar -S app/src/main/res/ -A app/src/main/assets
-F app/src/main/out/resources
这里依然用到了aapt工具,如果有assets文件需要加进来。成功后会在out目录下看到resources文件。
8. 生成资源APK和编译后的xml文件
这一步要编译xml文件为二进制文件,同时生成资源APK。
aapt package -f -M app/src/main/AndroidManifest.xml
-I android.jar -S app/src/main/res/ -A app/src/main/assets
-F app/src/main/out/res.apk
这里只是输出结果变成了APK,成功的话在out目录下会看到res.apk。
9. 把代码打入到APK里
上一步已经生成了资源APK,但没有代码,这一步我们把dex文件和resources打入到APK中。早期是直接使用apkbuilder工具直接打包,但是目前sdk将该工具移除了。其实apkbuilder最终调用的是sdklib.jar中的com.android.sdklib.build.ApkBuilderMain类来做事情的,所以这里直接通过 java <class地址> 使用ApkBuilderMain这个类,把resources和classes.dex加入到apk文件中。
java -classpath /Users/peizhifei/Library/Android/sdk/tools/lib/sdklib.jar
com.android.sdklib.build.ApkBuilderMain
app/src/main/out/res.apk -v -u -z
app/src/main/out/resources -f
app/src/main/build/classes.dex
成功的话可以打开res.apk,看到dex文件已经打进去了。
10. 签名APK
apk安装必须要签名,我们使用jarsigner给apk手动签名,为了方便可以使用debug签名。
jarsigner -verbose -keystore ~/.android/debug.keystore
-storepass android -keypass android
app/src/main/out/res.apk androiddebugkey
jarsigner的命令格式是这样的;
jarsigner -verbose -keystore <签名.keystore> -signedjar <签名ed.apk> <未签名.apk> <别名>
11. 对齐优化
至此APK就已经生成了,可以安装在手机上了,但是正常项目还是需要进行对齐优化的,能加快apk解压速度,需要用到zipalign工具。
zipalign -f 4 app/src/main/out/res.apk app/src/main/out/res_ zipalign.apk
成功后对齐的apk就是out目录下res_ zipalign.apk。
12. 安装并启动
这里可以安装APK到手机并启动主界面
adb install -r app/src/main/out/res2.apk
adb shell am start -n
com.example.peizhifei.myapplication/.MainActivity
这里用了强制安装,这样就能覆盖安装。
到此我们就大概实现了apk的打包过程,当然在实际项目中,要比上边的流程更复杂写,包括多module依赖,so文件引用,代码混淆等。
最后把所有这些命令都可以统一放到一个shell脚本里去执行。
为了路径简短,我把android.jar放到了app/src/main/下面,并进入到该目录,避免每次输入app/src/main/。
#进入项目目录
cd app/src/main/
#创建辅助目录
mkdir -p {gen,build,out}
#生成R文件
aapt p -f -M AndroidManifest.xml -I android.jar -S res -J gen -m
#编译aidl文件
aidl -Iapp/src aidl/com/example/peizhifei/myapplication/IMyAidlInterface.aidl
#编译得到class文件
javac -source 1.8 -target 1.8 -encoding UTF-8 -bootclasspath android.jar -d build java/com/example/peizhifei/myapplication/*.java gen/com/example/peizhifei/myapplication/*.java aidl/com/example/peizhifei/myapplication/*.java
#缺一步混淆
#将class文件生成dex文件
dx --dex --output=build/classes.dex build/
#生成资源映射表
aapt package -f -M AndroidManifest.xml -I android.jar -S res -A assets -F out/resources
#生成资源和二进制文件
aapt package -f -M AndroidManifest.xml -I android.jar -S res -A assets -F out/res.apk
#把代码打入到资源里
java -classpath /Users/peizhifei/Library/Android/sdk/tools/lib/sdklib.jar com.android.sdklib.build.ApkBuilderMain out/res.apk -v -u -z out/resources -f build/classes.dex
#签名APK
jarsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android out/res.apk androiddebugkey
#对齐优化
zipalign -f 4 out/res.apk out/res_zipalign.apk
#强制安装
adb install -r out/res_zipalign.apk
#启动主界面
adb shell am start -n com.example.peizhifei.myapplication/.MainActivity
直接执行该脚本,手机上就会启动主界面,简单总结一下。
aapt是一个重要的工具,来生成R.java文件和resource.arsc。assets是不需要做任何处理的,res/raw只需分配id后与assets一起直接打包到应用程序中,其它xml文件则会被编译成二进制文件。编译过程中,会把xml中的字符串进行收集去重,形成字符串资源池,元素中用到字符串的地方将被替换成相应的索引。另外标签属性值都会转换为资源id,进一步减少文件大小。二进制格式的xml把标签属性值转换为资源id后,避免了字符串解析,从而提高了解析速度。
另外就是生成资源APK后需要把代码打入到APK里面,然后对APK做一些优化处理即可。其实这个演示更多是为了加深对编译过程的理解,比如插件化处理资源就会从aapt入手解决资源冲突,汉化可以考虑修改resource.arsc并进行二次打包等。
本文介绍了APK的打包过程,实际操作演示了一下,感谢大家的阅读,我们下周再见。