oxA01【Android应用框架】Apk是如何生成的

前言

在Android Studio中直接点击 Run ‘app’ 就能够在build/outputs/apk生成能够在android设备中安装的Apk文件,那么Apk生成的过程是怎么样的呢?
html

Apk文件大概能够分为两个部分:代码和资源,因此打包的也分为代码和资源两个部分,咱们能够根据Google提供的流程图来具体了解一个Apk的构建过程java

新版构建流程图android

15813472877044-w350

APK打包的内容主要有:应用模块也就是本身开发的用到的源代码、资源文件、aidl接口文件,还有就是依赖模块即源代码用到的第三方依赖库如:aar、jar、so文件
数组

为了可以清楚的了解Apk是如何生成的, 来看一下老版构建流程图安全

老版构建流程图 bash

2019-03-22-15532697195669-w350

在了解Apk生成的过程以前,咱们须要了解一下图中各个工具的做用app

工具

名字 功能
AAPT/APT2 Android资源打包工具
AIDL 将全部的AIDL接口转化为java接口
Javac(Java Compiler) 将全部的Java代码编译成Class文件
Dex 将Class文件编译成Dex文件
Apkbuilder 将处理后的资源和代码打包生成Apk文件
Jarsigner/Apksigner 对未签名的apk文件进行签名
Zipalign 优化签名后的Apk,减小运行时所占用的内存

构建过程

1. 使用AAPT工具生成R.java文件

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 的目录
复制代码

2. 全部的AIDL接口转化为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文件路径以前设置
复制代码

3. 将Java代码编译成Class文件

使用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
复制代码

4. 将Class文件编译成Dex文件

使用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文件到具体位置
复制代码

5. 打包生成Apk文件

使用Apkbuilder(主要用到的是sdk/tools/lib/sdklib.jar文件中的ApkBuilderMain类)将全部的Dex文件、Resource.arsc、Res文件夹、Assets文件夹、AndroidManifest.xml 打包生成Apk文件(未签名)

6. 对Apk文件签名

使用Apksigner(Android官方针对apk签名及验证工具)或 Jarsigner(JDK提供针对jar包签名工具)对未签名的apk文件进行签名

ps:若是使用Apksigner签名须要(7. 优化Apk文件)放到(6. 对Apk文件签名)签名前面,为何?请查看关于Apksigner 和 Jarsigner的区别,请移步到文末

7. 优化Apk文件

使用zipalign对签名后的apk文件进行对齐处理,对齐的主要过程是将APK包中全部的资源文件距离文件起始偏移为4字节整数倍,这样经过内存映射访问apk文件时的速度会更快,减小其在设备上运行时所占用的内存

总结

上述打包过程都是AndroidStudio编译时,调用各类编译命令自动完成的, 总结一下上述打包过程:

  1. 除了assets和res/raw资源被原装不动地打包进APK以外,其它的资源都会被编译或者处理
  2. 除了assets资源以外,其它的资源都会被赋予一个资源ID
  3. 打包工具负责编译和打包资源,编译完成以后,会生成一个resources.arsc文件和一个R.java,前者保存的是一个资源索引表,后者定义了各个资源ID常量
  4. 应用程序配置文件AndroidManifest.xml一样会被编译成二进制的XML文件,而后再打包到APK里面去
  5. 应用程序在运行时经过AssetManager来访问资源,或经过资源ID来访问,或经过文件名来访问

Apk文件大概能够分为两个部分:代码和资源, 代码部分经过Javac将Java代码编译成Class文件, 而后经过DX工具将Class文件编译成Dex文件,接下来咱们主要来分析一下资源的编译和打包

资源的编译和打包

在分析资源的编译和打包以前,咱们须要了解一下Android都有哪些资源,其实Android资源大概分为两个部分:assets 和 res

1. assets资源

assets资源放在assets目录下,它里面保存一些原始的文件,能够以任何方式来进行组织,这些文件最终会原封不动的被打包进APK文件中,经过AssetManager来获取asset资源,代码以下

AssetManager assetManager = context.getAssets();
InputStream is = assetManager.open("fileName");
复制代码

2. res资源

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工具来完成的,资源打包主要有如下几个流程:

  1. 解析AndroidManifest.xml,得到应用程序的包名称,建立资源表
  2. 添加被引用资源包,被添加的资源会以一种资源ID的方式定义在R.java中
  3. 资源打包工具建立一个AaptAssets对象,收集当前须要编译的资源文件,收集到的资源保存在AaptAssets对象对象中
  4. 将上一步AaptAssets对象保存的资源,添加到资源表ResourceTable中去,用于最终生成资源描述文件resources.arsc
  5. 编译values类资源,这类资源包括数组、颜色、尺寸、字符串等值
  6. 给style、array这类资源分配资源ID
  7. 编译xml资源文件,编译的流程分为:① 解析xml文件 ② 赋予属性名称资源ID ③ 解析属性值 ④ 将xml文件从文本格式转换为二进制格式
  8. 生成资源索引表resources.arsc
2.1 资源ID

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不变,这样就永远不会有冲突发生了

2.1 资源索引(resources.arsc)

最终生成的是资源索引表resources.arsc,resources.arsc是一个编译后的二进制文件, 在AndroidStudio打开resources.arsc文件,以下所示

Android正是利用这个索引表根据资源ID进行资源的查找,为不一样语言、不一样地区、不一样设备提供相对应的最佳资源。查找和经过Resources和 AssetManger来完成的

在文中提到了两个工具Apksigner 和 Jarsigner,下面一块儿来了解一下Apksigner 和 Jarsigner的区别

Apksigner 和 Jarsigner的区别

在Android Studio中点击菜单 Build->Generate signed apk... 打包签名过程当中,能够看到两种签名选项 V1(Jar Signature) V2(Full APK Signature)

  • Jarsigner是JDK提供的针对jar包签名的通用工具
  • Apksigner是Google官方提供的针对Android apk签名及验证的专用工具

从Android 7.0开始, 谷歌增长新签名方案 V2 Scheme (APK Signature),但Android 7.0如下版本, 只能用旧签名方案 V1 scheme (JAR signing)

V1(Jar Signature)签名:

来自JDK(Jarsigner),对zip压缩包的每一个文件进行验证, 签名后还能对压缩包修改(移动/从新压缩文件),对V1签名的apk/jar解压,在META-INF存放签名文件(MANIFEST.MF, CERT.SF, CERT.RSA), 其中MANIFEST.MF文件保存全部文件的SHA1指纹(除了META-INF文件), 由此可知: V1签名是对压缩包中单个文件签名验证

V2(Full APK Signature)签名:

来自Google(apksigner), 对zip压缩包的整个文件验证, 签名后不能修改压缩包(包括zipalign), 对V2签名的apk解压, 没有发现签名文件, 从新压缩后V2签名就失效, 由此可知: V2签名是对整个APK签名验证

建立发布密钥库,请参阅在 Android Studio 中为应用签名

总结
  • V1签名是对压缩包中单个文件签名验证
  • V2签名是对整个APK签名验证
  • zipalign能够在V1签名后执行
  • zipalign不能在V2签名后执行,只能在V2签名以前执行
  • V2签名更安全(不能修改压缩包)
  • V2签名验证时间更短(不须要解压验证), 于是安装速度加快

注意: apksigner工具默认同时使用V1和V2签名,以兼容Android 7.0如下版本

参考

相关文章
相关标签/搜索