分析Android中APK的打包流程

在实际的开发工作中,几乎每次都是在编译器中直接运行,甚至完成对apk的签名,其实隐藏在运行按钮后面的是一系列构建工具的调用,这些工具有的是Android SDK提供的build tools,有的是Gradle提供的plugins,还有Java JDK中的工具。这些工具的调用顺序是怎样的,或者说一个完整的apk打包流程是怎样的?

Google官网中给出了一个apk打包的流程图:
apk打包流程

从图中,我们可以将一个apk打包的流程,整理为以下几个步骤:

  1. Android SDK中的appt工具将资源文件进行编译,生成编译后的资源文件和一份R.java 文件;
  2. Android SDK中的aidl工具将定义的一些aidl借口转化为java 接口;
  3. 通过JDK中的javac将前面两步中生成的R.java、java接口文件,和我们编写的业务代码编译成.class文件;
  4. Android SDK中的dex工具将第3步中生成的.class文件,以及项目工程中依赖的一些三方库转化为.dex文件;
  5. Gradle中的apkbuilder插件将第1步生成的编译后的资源文件,第4步中的.dex文件和项目中的一些其它的资源文件进行编译,最终转化成一个未签名的.apk文件;
  6. 通过JDK中的jarsigner对第5步生成的.apk文件进行签名;
  7. 通过Android SDK中的zipalign工具对签名的apk文件进行优化。

至此,一个的apk文件的打包流程就完成了。看完上面这个流程,也许会有一些疑问,比如混淆(proguard)过程是在什么时候完成的呢?还有我们在项目中会指定compileSDKVersion「Android Pie开始不再需要指定compileSDKVersion」,不同的版本号与打包流程有什么关联呢?aapt工具对哪些资源文件进行了编译呢?等等。下面对apk的打包过程给出一个更为详尽的介绍。

apk详细的打包流程

下图是一个完整的apk打包的流程,在这个流程中涉及到Gradle plugins,Android SDK中的工具及JDK中工具的使用,流程图中对不同资源文件,生成的文件,及工具进行了颜色的区分。下面对apk打包流程进行具体的分析,图中还涉及了三个扩展的流程,在后面会进行简单的介绍。
apk详细的打包流程

step1 aapt打包资源文件,生成二进制文件和R.java

AAPT是Android Asset Packing Tool的缩写,Android SDK提供的资源打包工具,aapt工具所在位置:

1
..\android-sdk\build-tools\<buildToolsVersion>

aapt工具会对AndroidManifest.xml,assets资源,res资源进行编译,其中res资源文件中res/drawable(非Bitmap文件,即非.png、.9.png、.jpg、.gif文件)均会从文本格式的XML文件编译成二进制格式的XML文件。

为什么要编译成二进制文件

  • 二进制格式的XML文件占用空间更小。这是由于所有XML元素的标签、属性名称、属性值和内容所涉及到的字符串都会被统一收集到一个字符串资源池中去,并且会去重。有了这个字符串资源池,原来使用字符串的地方就会被替换成一个索引到字符串资源池的整数值,从而可以减少文件的大小。
  • 二进制格式的XML文件解析速度更快。这是由于二进制格式的XML元素里面不再包含有字符串值,因此就避免了进行字符串解析,从而提高速度。

编译assets资源

assets资源放在工程根目录的assets子目录下,它里面保存的是一些原始的文件,可以以任何方式来进行组织。这些文件最终会被原装不动地打包在apk文件中。

编译res资源

目录中的res/animator、res/anim、res/color、res/drawable(非Bitmap文件,即非.png、.9.png、.jpg、.gif文件)、res/layout、res/menu、res/values和res/xml等都会被编译,并且都会被赋予资源ID。这样我们就可以在程序中通过ID来访问res类的资源。res类资源按照不同的用途可以进一步划分为以下10种子类型 见附录1

为了使得一个应用程序能够在运行时同时支持不同的大小和密度的屏幕,以及支持国际化,即支持不同的国家地区和语言,Android应用程序资源的组织方式有19个维度 见附录2,每一个维度都代表一个配置信息,从而可以使得应用程序能够根据设备的当前配置信息来找到最匹配的资源来展现在UI上,从而提高用户体验。

由于Android应用程序资源的组织方式可以达到19个维度,因此就要求Android资源管理框架能够快速定位最匹配设备当前配置信息的资源来展现在UI上,否则的话,就会影响用户体验。为了支持Android资源管理框架快速定位最匹配资源,Android资源打包工具aapt在编译和打包资源的过程中,会执行以下两个额外的操作:

  • 赋予每一个非assets资源一个ID值,这些ID值以常量的形式定义在一个R.java文件中。
  • 生成一个resources.arsc文件,用来描述那些具有ID值的资源的配置信息,它的内容就相当于是一个资源索引表。包含了所有的id值的数据集合。在该文件中,如果某个id对应的是string,那么该文件会直接包含该值,如果id对应的资源是某个layout或者drawable资源,那么该文件会存入对应资源的路径。

有了资源ID以及资源索引表之后,Android资源管理框架就可以迅速将根据设备当前配置信息来定位最匹配的资源了。

编译AndroidManifest.xml

应用程序配置文件AndroidManifest.xml的编译过程与其它的Xml资源文件的编译过程是一样的,应用程序配置文件AndroidManifest.xml编译完成之后,Android资源打包工具appt还会验证它的完整性和正确性,例如,验证AndroidManifest.xml的根节点mainfest必须定义有android:package属性。

step2 aidl 接口定义文件的转化

AIDL是Android Interface Definition Language的缩写,即接口定义语言,主要是用来完成Android中进程间的通信(IPC),这主要是因为Android应用之间不能共享内存。在写.aidl文件时需要注意两点:

  • 需要进行通信的应用中,AIDL接口文件所在的包必须相同;
  • AIDL通常都需要配合Service来实现业务逻辑;

Android中的aidl工具将我们定义的.aidl文件转化为Java代码。aidl工具所在位置:

1
..\android-sdk\build-tools\<buildToolsVersion>

step3 java compiler代码编译

使用JDK中的javac工具,将step1中生成的R.java文件,step2中生成的java代码,我们编写的业务逻辑代码,依赖的Android库文件及Android编译版本对应的android.jar一起进行编译,最终输出.class文件。

step4 proguard代码混淆

为了保护我们编写的业务逻辑代码,有时需要对step3中生成的.class文件进行混淆。进行代码混淆会用到proguard插件。proguard主要完成4个功能:

  • 压缩(Shrink):检测并移除代码中无用的类、字段、方法和特性(Attribute);
  • 优化(Optimize):对字节码进行优化,移除无用的指令;
  • 混淆(Obfuscate):使用a,b,c,d这样简短而无意义的名称,对类、字段和方法进行重命名。
  • 预检(Preveirfy):在Java平台上对处理后的代码进行预检,确保加载的class文件是可执行的。

对step3中.class文件进行混淆之后,得到混淆之后的.class文件及一份mapping文件,mapping文件列出了原始的类,方法和字段名与混淆后代码间的映射,有了这份映射可以将混淆之后的代码进行还原。

step5 dex将.class文件转化为.dex

使用Android SDK中的dx工具,将step4中生成的.class文件转化为.dex文件。在转化的过程中,dx工具主要是对Java类文件重新排列,消除在类文件中出现的所有冗余信息,避免虚拟机在初始化时出现反复的文件加载与解析过程。

为什么要转化成.dex文件,而不是直接使用.class文件?这要从移动设备的虚拟机Dalvik说起。

Dalvik虚拟机

Dalvik是Google公司自己设计用于Android平台的Java虚拟机,它是Android平台的重要组成部分,支持dex格式(Dalvik Executable)的Java应用程序的运行。dex格式是专门为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。Google对其进行了特定的优化,使得Dalvik具有高效、简洁、节省资源的特点。

概括起来,Dalvik具有以下几个特点:

  1. 体积小,占用内存空间小;
  2. 专有的DEX可执行文件格式,体积更小,执行速度更快;
  3. 常量池采用32位索引值,寻址类方法名,字段名,常量更快;
  4. 基于寄存器架构,并拥有一套完整的指令系统;
  5. 提供了对象生命周期管理,堆栈管理,线程管理,安全和异常管理以及垃圾回收等重要功能;
  6. 所有的Android程序都运行在Android系统进程里,每个进程对应着一个Dalvik虚拟机实例。

再来说一下,和Java虚拟机相比,二者到底有哪些不同呢?

  1. 最直观的一点,Java虚拟机运行的是Java字节码,Dalvik虚拟机运行的是Dalvik字节码。
  2. Dalvik可执行文件体积小。Android SDK中dx的工具负责将Java字节码转换为Dalvik字节码。
  3. 二者最大的区别是所采用的架构不同,Java虚拟机基于栈架构,程序在运行时虚拟机需要频繁的从栈上读取或写入数据,这个过程需要更多的指令分派与内存访问次数,会耗费不少CPU时间。Dalvik虚拟机基于寄存器架构。数据的访问通过寄存器间直接传递,这样的访问方式比基于栈方式要快很多。

ART虚拟机

Android Runtime (ART) 在Android 5.0(API 级别 21)及更高版本的设备是默认运行的。既然有了Dalvik虚拟机,为什么还要引入ART虚拟机呢?先来了解一下什么是ART虚拟机。

ART代表Android Runtime,其处理应用程序执行的方式完全不同于Dalvik,Dalvik是依靠一个Just-In-Time (JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。ART则完全改变了这套做法,在应用安装时就预编译字节码到机器语言,这一机制叫Ahead-Of-Time (AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。

原来与Dalvik虚拟机相比,ART虚拟机的效率更高,速度更快,具体是怎样做到性能提升的呢?在dalvik中,如同其他大多数JVM一样,都采用的是JIT来做及时翻译(动态翻译),将dex或odex中并排的dalvik code(或者叫smali指令集)运行态翻译成native code去执行.JIT的引入使得dalvik提升了3~6倍的性能。

而在ART中,完全抛弃了dalvik的JIT,使用了AOT直接在安装时将其完全翻译成native code.这一技术的引入,使得虚拟机执行指令的速度又一重大提升,说的直接一点,使用空间换时间。虽然使用ART虚拟机会比Dalvik占用更多的空间,但并不意味着内存管理效率就会降低。官方统计,ART的内存利用率与Dalvik相比提高10倍了左右。

最后从Dalvik虚拟机对比的角度,对ART虚拟机的特点进行一下总结:

  1. 系统性能的显著提升;
  2. 应用启动更快、运行更快、体验更流畅、触感反馈更及时;
  3. 更长的电池续航能力;
  4. 支持更低的硬件;
  5. 更大的存储空间占用,可能会增加10%-20%;
  6. 更长的应用安装时间;

step6 apkbuilder打包成APK

apkbuilder工具会将step1中编译生成的二进制资源文件,step5中生成的.dex文件,以及依赖的一下java资源文件(如.so等),转化为apk文件。需要注意的是,apkbuilder在 Android SDK build tools r22里面被移除了。现在使用到的apkbuilder是sdklib.jar的ApkBuilder类,具体位置:

1
..\android-sdk\tools\sdklib-<buildToolsVersion>.jar

step7 jarsigner签名

使用jarsigner对step6中的apk文件进行签名,这是由于Android系统在安装APK的时候,首先会检验APK的签名,如果发现签名文件不存在或者校验签名失败,则会拒绝安装,所以应用程序在发布之前一定要进行签名。

签名的好处

给apk签名的好处,总结起来有以下几点:

  1. 如果想无缝升级一个应用,Android系统要求应用程序的新版本与老版本具有相同的签名与包名。若包名相同而签名不同,系统会拒绝安装新版应用。
  2. Android系统可以允许同一个证书签名的多个应用程序在一个进程里运行,系统实际把他们作为一个单个的应用程序。此时就可以把我们的应用程序以模块的方式进行部署,而用户可以独立的升级其中的一个模块。
  3. Android提供了基于签名的权限机制,一个应用程序可以为另一个以相同证书签名的应用程序公开自己的功能与数据,同时其它具有不同签名的应用程序不可访问相应的功能与数据。
  4. 签名信息中包含有开发者信息,在一定程度上可以防止应用被伪造。例如网易云加密对Android APK加壳保护中使用的“校验签名(防二次打包)”功能就是利用了这一点。

签名的原理

对一个APK文件签名之后,APK文件根目录下会增加META-INF目录,该目录下增加三个文件,Android系统就是根据这三个文件的内容对APK文件进行签名检验的。

1
2
3
MANIFEST.MF
NETEASE.RSA
NETEASE.SF

其中MANIFEST.MF中保存了APK里所有文件的SHA1校验值的BASE64编码,SF文件里保存了MANIFEST.MF文件的SHA1校验值的BASE64编码,同时还保存了MANIFEST.MF中每一条记录的SHA1检验值BASE64编码,RSA文件则包含了签名的公钥、签名所有者等信息,还保存了用SHA1withRSA签名算法对SF文件的签名结果信息。

step8 zipalign优化

使用Android SDK中的zipalign工具对签名的apk文件进行优化。zipalign工具位置:

1
..\android-sdk\build-tools\<buildToolsVersion>

zipalign工具能够对打包的应用程序进行优化,使得在运行时Android与应用程序间的交互更加有效率,能够让应用程序和整个系统运行得更快。

Android系统中Application的数据都保存在它的APK文件中,同时可以被多个进程访问,安装的过程包括如下几个步骤:

  1. Installer通过每个apk的manifest文件获取与当前应用程序相关联的permissions信息;
  2. Home application读取当前APK的Name和Icon等信息;
  3. System server将读取一些与Application运行相关信息,例如:获取和处理Application的notifications请求等。
  4. 最后,APK所包含的内容不仅限于当前Application所使用,而且可以被其它的Application调用,提高系统资源的可复用性。

当资源文件通过内存映射对齐到4字节边界时,访问资源文件的代码效率比较高。如果资源本身没有进行对齐处理,只能进行显式地读取,这个过程将会比较缓慢且会花费额外的内存。

至此,一个完整的apk的打包流程就介绍完了,在apk详细打包流程图中,还有两个扩展的流程,分别是NDK工作流程,RS Support Mode工作流程。

在NDK工作流程中,我们编写的JNI代码与NDK中的NDK Headers文件,通过Android SDK中提供的ndk工具编译成.so文件,这些.so文件与项目中依赖的.so文件一起参与到step6 apkbuilder的打包过程。

在RS Support Mode工作流程中,RS文件,与NDK中的RS Headers文件,以及依赖的Android库中的RS Headers文件,通过Android SDK中的llvm-rs-cc工具,编译生成java class文件和Bitcode文件,java class文件会参与到step3 javac的编译过程,Bitcode文件与依赖的RS 库文件通过bcc compact工具编译成.o文件,.o文件再通过linker工具编译成.so文件,最终,这些.so文件参与到step6 apkbuilder的打包过程。

附录

附录1 res类资源的10种子类型

编号 res资源类型 描述
1 animator 这类资源以XML文件保存在res/animator目录下,用来描述属性动画。属性动画通过改变对象的属性来实现动画效果,例如,通过不断地修改对象的坐标值来实现对象移动动画,又如,通过不断地修改对象的Alpha通道值来实现对象的渐变效果。
2 anim 这类资源以XML文件保存在res/anim目录下,用来描述补间动画。补间动画和属性动画不同,它不是通过修改对象的属性来实现,而是在对象的原来形状或者位置的基础上实现一个变换来得到的,例如,对对象施加一个旋转变换,就可以获得一个旋转动画,又如,对对象实施一个缩放变换,就可以获得一个缩放动画。从数学上来讲,就是在对象的原来形状或者位置的基础上施加一个变换矩阵来实现动画效果。注意,在动画的执行过程中,对象的属性是始终保持不变的,我们看到的只不过是它的一个变形副本。
3 color 这类资源以XML文件保存在res/color目录下,用描述对象颜色状态选择子。例如,我们可以定义一个选择子,规定一个对象在不同状态下显示不同的颜色。对象的状态可以划分为pressed、focused、selected、checkable、checked、enabled和window_focused等7种。
4 drawable 这类资源以XML或者Bitmap文件保存在res/drawable目录下,用来描述可绘制对象。例如,我们可以在里面放置一些图片(.png, .9.png, .jpg, .gif),来作为程序界面视图的背景图。注意,保存在这个目录中的Bitmap文件在打包的过程中,可能会被优化的。例如,一个不需要多于256色的真彩色PNG文件可能会被转换成一个只有8位调色板的PNG面板,这样就可以无损地压缩图片,以减少图片所占用的内存资源。
5 mipmap mipmap文件夹中只能存放应用程序的启动图标文件,即安装后在主屏幕上显示的图标文件,其它的图片资源还要放在drawable目录下。使用时的引用名称不再是R.drawable.icon_name,而是R.mipmap.icon_name。放在mipmap中的启动图标的分辨率与设备的屏幕密度无关。
6 layout 这类资源以XML文件保存在res/layout目录下,用来描述应用程序界面布局。
7 menu 这类资源以XML文件保存在res/menu目录下,用来描述应用程序菜单,例如,Options Menu、Context Menu和Sub Menu。
8 raw 这类资源以任意格式的文件保存在res/raw目录下,它们和assets类资源一样,都是原装不动地打包在apk文件中的,不过它们会被赋予资源ID,这样我们就可以在程序中通过ID来访问它们即 R.raw.filename)。如需访问原始文件名和文件层次结构,则可以考虑将某些资源保存在 assets/ 目录下(而不是 res/raw/)。assets/ 中的文件没有资源 ID,因此只能使用 AssetManager 读取这些文件。
9 values 这类资源以XML文件保存在res/values目录下,用来描述一些简单值,例如,数组、颜色、尺寸、字符串和样式值等,一般来说,这六种不同的值分别保存在名称为arrays.xml、colors.xml、dimens.xml、strings.xml和styles.xml文件中。
10 xml 这类资源以XML文件保存在res/xml目录下,一般就是用来描述应用程序的配置信息。

附录2 Android资源组织方式的19个维度

序号 配置 限定值 描述
1 MCC 和 MNC 示例:mcc310、mcc310-mnc004、mcc208-mnc00等等 移动国家代码 (MCC),(可选)后跟设备 SIM 卡中的移动网络代码 (MNC)。例如,mcc310 是指美国的任一运营商,mcc310-mnc004 是指美国的 Verizon 公司,mcc208-mnc00 是指法国的 Orange 公司。
2 语言和区域 示例:en、fr、en-rUS、fr-rFR、fr-rCA等等 语言通过由两个字母组成的 ISO 639-1 语言代码定义,可以选择后跟两个字母组成的 ISO 3166-1-alpha-2 区域码(前带小写字母“r”)。这些代码不区分大小写;r 前缀用于区分区域码。 不能单独指定区域。
3 布局方向 ldrtl、ldltr 应用的布局方向。ldrtl 是指“布局方向从右到左”。ldltr 是指“布局方向从左到右”,这是默认的隐式值。它适用于布局、图片或值等任何资源。例如,若要针对阿拉伯语提供某种特定布局,并针对任何其他“从右到左”语言(如波斯语或希伯来语)提供某种通用布局,如 layout-ar/(指定用于阿拉伯语的layout) 、 layout-ldrtl/ (从右至左的语言)
4 smallestWidth swdp 示例:sw320dp、sw600dp、sw720dp等等 屏幕的基本尺寸,由可用屏幕区域的最小尺寸指定。 具体来说,设备的 smallestWidth 是屏幕可用高度和宽度的最小尺寸(您也可以将其视为屏幕的“最小可能宽度”)。无论屏幕的当前方向如何,您均可使用此限定符确保应用 UI 的可用宽度至少为 dp。例如,如果布局要求屏幕区域的最小尺寸始终至少为 600dp,则可使用此限定符创建布局资源 res/layout-sw600dp/。仅当可用屏幕的最小尺寸至少为 600dp 时,系统才会使用这些资源,而不考虑 600dp 所代表的边是用户所认为的高度还是宽度。smallestWidth 是设备的固定屏幕尺寸特性;设备的 smallestWidth 不会随屏幕方向的变化而改变。
5 可用宽度 wdp示例:w720dp、w1024dp等等 指定资源应该使用的最小可用屏幕宽度,以 dp 为单位,由 值定义。在横向和纵向之间切换时,为了匹配当前实际宽度,此配置值也会随之发生变化。
6 可用高度 hdp 示例:h720dp、h1024dp等等 指定资源应该使用的最小可用屏幕高度,以“dp”为单位,由 值定义。 在横向和纵向之间切换时,为了匹配当前实际高度,此配置值也会随之发生变化。
7 屏幕尺寸 small、normal、large、xlarge small:尺寸类似于低密度 QVGA 屏幕的屏幕。小屏幕的最小布局尺寸约为 320x426 dp 单位。例如,QVGA 低密度屏幕和 VGA 高密度屏幕。normal:尺寸类似于中等密度 HVGA 屏幕的屏幕。标准屏幕的最小布局尺寸约为 320x470 dp 单位。例如,WQVGA 低密度屏幕、HVGA 中等密度屏幕、WVGA 高密度屏幕。large:尺寸类似于中等密度 VGA 屏幕的屏幕。 大屏幕的最小布局尺寸约为 480x640 dp 单位。 例如,VGA 和 WVGA 中等密度屏幕。xlarge:明显大于传统中等密度 HVGA 屏幕的屏幕。超大屏幕的最小布局尺寸约为 720x960 dp 单位。在大多数情况下,屏幕超大的设备体积过大,不能放进口袋,最常见的是平板式设备。 API 级别 9 中的新增配置。
8 屏幕纵横比 long、notlong long:宽屏,如 WQVGA、WVGA、FWVGA;notlong:非宽屏,如 QVGA、HVGA 和 VGA
9 圆形屏幕 round、notround round:圆形屏幕,例如圆形可穿戴式设备;notround:方形屏幕,例如手机或平板电脑
10 屏幕方向 port、land port:设备处于纵向(垂直);land:设备处于横向(水平)
11 UI 模式 car、desk、television、appliance、watch car:设备正在车载手机座上显示;desk:设备正在桌面手机座上显示;television:设备正在电视上显示,为用户提供“十英尺”体验,其 UI 位于远离用户的大屏幕上,主要面向方向键或其他非指针式交互;appliance:设备用作不带显示屏的装置;watch:设备配有显示屏,戴在手腕上。
12 夜间模式 night、notnight night:夜间;notnight:白天
13 屏幕像素密度 (dpi) ldpi、mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi、nodpi、tvdpi、anydpi ldpi:低密度屏幕;约为 120dpi。mdpi:中等密度(传统 HVGA)屏幕;约为 160dpi。hdpi:高密度屏幕;约为 240dpi;xhdpi:超高密度屏幕;约为 320dpi。此项为 API 级别 8 中新增配置;xxhdpi:超超高密度屏幕;约为 480dpi。此项为 API 级别 16 中新增配置;xxxhdpi:超超超高密度屏幕使用(仅限启动器图标);约为 640dpi。 此项为 API 级别 18 中新增配置;nodpi:它可用于您不希望缩放以匹配设备密度的位图资源。tvdpi:密度介于 mdpi 和 hdpi 之间的屏幕;约为 213dpi。它并不是“主要”密度组, 主要用于电视,而大多数应用都不需要它。对于大多数应用而言,提供 mdpi 和 hdpi 资源便已足够,系统将根据需要对其进行缩放。此项为 API 级别 13 中新增配置;anydpi:此限定符适合所有屏幕密度,其优先级高于其他限定符。 这对于矢量可绘制对象很有用。 此项为 API 级别 21 中新增配置。
14 触摸屏类型 notouch、finger notouch:设备没有触摸屏。finger:设备有一个专供用户通过手指直接与其交互的触摸屏。
15 键盘可用性 keysexposed、keyshidden、keyssoft keysexposed:设备具有可用的键盘。如果设备启用了软键盘(不无可能),那么即使硬键盘没有展示给用户,哪怕设备没有硬键盘,也可以使用此限定符。 如果没有提供或已经禁用软键盘,则只有在显示硬键盘时才会使用此限定符。keyshidden:设备具有可用的硬键盘,但它处于隐藏状态,且设备没有启用软键盘。keyssoft:设备已经启用软键盘(无论是否可见)。
16 主要文本输入法 nokeys、qwerty、12key nokeys:设备没有用于文本输入的硬按键。qwerty:设备具有标准硬键盘(无论是否对用户可见)。12key:设备具有 12 键硬键盘(无论是否对用户可见)。
17 导航键可用性 navexposed、navhidden navexposed:导航键可供用户使用。navhidden:导航键不可用(例如,位于密封盖子后面)
18 主要非触摸导航方法 nonav、dpad、trackball、wheel nonav:除了使用触摸屏以外,设备没有其他导航设施。dpad:设备具有用于导航的方向键。trackball:设备具有用于导航的轨迹球。wheel:设备具有用于导航的方向盘(不常见)。
19 平台版本(API 级别) 示例:v3、v4、v7等等 设备支持的 API 级别。例如,v1 对应于 API 级别 1(带有 Android 1.0 或更高版本系统的设备),v4 对应于 API 级别 4(带有 Android 1.6 或更高版本系统的设备)。

参考资料

Android应用程序资源的编译和打包过程分析
APK打包安装过程
Android APK签名原理及方法
https://developer.android.com/studio/command-line/aapt2
https://developer.android.com/guide/practices/verifying-apps-art



   

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×