Android项目构建过程分析

前言

2017年了,再来谈构建过程彷佛是个too young , too simple的问题了。在准备面试的过程当中,对这个部分有过较为认真的学习,也进行了博客记录,可是实际工做过程当中,若是是在写业务逻辑上,那么这方面的问题接触的就会比较少了。逐渐的淡忘了,其次,以前所写的文章条理性也不是很强,同时,最近准备进行Gradle插件的一系列博客的产出,其中将会涉及到不少与项目构建相关的内容。因此此文也将成为后续文章的一个铺垫。html

构建过程

项目的构建:当咱们打开一个项目,咱们能够看到的是咱们写的Java Code文件or Other JVM Code,资源文件,Build配置文件,可是经过run the project,咱们就能够获得一个在咱们的Andoid设备上能够运行的Apk,上线应用市场,还须要咱们对其进行签名处理,来确保咱们App的惟一性和安全性。整个过程就是所谓的项目构建。以下图所示。java

如何实现整个构建的过程,对于每个构建的步骤,都须要相应的功能模块来进行,好比Java Code编译,如何打成dex包等等,而这Android则为咱们提供了相应的工具,在Android Studio命令行窗口中,咱们能够经过相应的命令行来进行控制,可是,整个构建过程涉及到不少的步骤,不少的工具的使用,若是都经过命令行来进行控制,势必会至关麻烦,所以Androd Studio等IDE则对整个过程进行了一个打包,当咱们在Run project的时候,底层的打包工具就会被调用,打包流程都会自动执行。而后咱们只须要对构建文件按照本身的需求进行相应的配置,就能够构建出本身所须要的项目。
那么,整个Andoid项目的构建过程当中,都执行了那些构建的任务呢?android

首先看一下,Google官方为咱们提供的详细的构建过程图面试

构建过程概述

若是你接触Android开发已经有一段时间了,我想当你看到这张图的时候,就会以为很清晰。可是更多的可能会一头雾水,若是以前没有阅读相关的资料的话,那么,接下来,将针对上述的构建过程,先给出一个概述,这样你将会整个构建流程在心中有一个框架,而后针对其中具体的细节,进行进一步详细的讲解。segmentfault

图中绿色标注为其中用到的相应工具,蓝色表明的是中间生成的各种文件类型。安全

  • 首先aapt工具会将资源文件进行转化,生成对应资源ID的R文件和资源文件。框架

  • adil工具会将其中的aidl接口转化成Java的接口工具

  • 至此,Java Compiler开始进行Java文件向class文件的转化,将R文件,Java源代码,由aidl转化来的Java接口,统一转化成.class文件。布局

  • 经过dx工具将class文件转化为dex文件。post

  • 此时咱们获得了通过处理后的资源文件和一个dex文件,固然,还会存在一些其它的资源文件,这个时候,就是将其打包成一个相似apk的文件。但还并非直接能够安装在Android系统上的APK文件。

  • 经过签名工具对其进行签名。

  •   经过Zipalign进行优化,提高运行速度(原理后文会说起)。

  • 最终,一个能够安装在咱们手机上的APK了。

经过上述讲解,我想对于Android项目的整个构建过程,应该有了一个很清晰的框架了,下面将针对其中的具体的细节,和前面挖的一些坑,来进行更细致的分析,下图是一个Android项目构建过程的详细步骤图。

详细构建过程

接下来的分析,咱们仍是按照上述构建过程概述的顺序和流程,进行具体的分析。

第1步:aapt打包资源文件,生成R.java和编译后的资源(二进制文件)

讲到资源文件的处理,咱们先来看一下Android中的资源文件有那些呢?Android应用程序资源能够分为两大类,分别是assets和res:
   1. assets类资源放在工程根目录的assets子目录下,它里面保存的是一些原始的文件,能够以任何方式来进行组织。这些文件最终会被原装不动地打包在apk文件中。若是咱们要在程序中访问这些文件,那么就须要指定文件名来访问。例如,假设在assets目录下有一个名称为filename的文件,那么就可使用如下代码来访问它:

AssetManager am= getAssets();    
InputStream is = assset.open("filename");  

   2. res类资源放在工程根目录的res子目录下,它里面保存的文件大多数都会被编译,而且都会被赋予资源ID。这样咱们就能够在程序中经过ID来访问res类的资源。res类资源按照不一样的用途能够进一步划分为如下10种子类型:
layout(布局文件),drawable,xml,value,menu,raw,color,anim,animator,mipmap。
为了使得一个应用程序可以在运行时同时支持不一样的大小和密度的屏幕,以及支持国际化,即支持不一样的国家地区和语言,Android应用程序资源的组织方式有18个维度,每个维度都表明一个配置信息,从而可使得应用程序可以根据设备的当前配置信息来找到最匹配的资源来展示在UI上,从而提升用户体验。因为Android应用程序资源的组织方式能够达到18个维度,所以就要求Android资源管理框架可以快速定位最匹配设备当前配置信息的资源来展示在UI上,不然的话,就会影响用户体验。为了支持Android资源管理框架快速定位最匹配资源,Android资源打包工具aapt在编译和打包资源的过程当中,会执行如下两个额外的操做:

  • 赋予每个非assets资源一个ID值,这些ID值以常量的形式定义在一个R.java文件中。

  • 生成一个resources.arsc文件,用来描述那些具备ID值的资源的配置信息,它的内容就至关因而一个资源索引表。包含了全部的id值的数据集合。在该文件中,若是某个id对应的是string,那么该文件会直接包含该值,若是id对应的资源是某个layout或者drawable资源,那么该文件会存入对应资源的路径。

为何要转化为二进制文件?

  • 二进制格式的XML文件占用空间更小。这是因为全部XML元素的标签、属性名称、属性值和内容所涉及到的字符串都会被统一收集到一个字符串资源池中去,而且会去重。有了这个字符串资源池,原来使用字符串的地方就会被替换成一个索引到字符串资源池的整数值,从而能够减小文件的大小。

  • 二进制格式的XML文件解析速度更快。这是因为二进制格式的XML元素里面再也不包含有字符串值,所以就避免了进行字符串解析,从而提升速度。
    有了资源ID以及资源索引表以后,Android资源管理框架就能够迅速将根据设备当前配置信息来定位最匹配的资源了。

对于具体的一些操做流程,能够参考本人以前的一篇文章APK打包安装过程或者更偏向于源码层级的老罗的文章。(文后参考文献连接)

第2步:aidl

aidl,全名Android Interface Definition Language,即Android接口定义语言。是咱们在编写进程间通讯的代码的时候,定义的接口。
输入:aidl后缀的文件。输出:可用于进程通讯的C/S端java代码,位于build/generated/source/aidl。

第3步:Java源码编译

咱们有了R.java和aidl生成的Java文件,再加上工程的源代码,如今可使用javac进行正常的java编译生成class文件了。

输入:java source的文件夹(另外还包括了build/generated下的:R.java, aidl生成的java文件,以及BuildConfig.java)。输出:对于gradle编译,能够在build/intermediates/classes里,看到输出的class文件。

第4步:代码混淆(proguard)

源码编译以后,咱们可能还会对其进行代码的混淆,混淆的做用是增长反编译的难度,同时也将一些代码的命名进行了缩短,减小代码占用的空间。混淆完成以后,会生成一个混淆先后的映射表,这个是用来在反应咱们的应用执行的时候的一些堆栈信息,能够将混淆后的信息转化为咱们混淆前实际代码中的内容。
而这个过程使用的工具就是ProGuard,是一个开源的Java代码混淆器(obfuscation)。ADT r8开始它被默认集成到了Android SDK中。 其具有三个主要功能。

  • 压缩 - 移除无效的类、属性、方法等

  • 优化 - 优化bytecode移除没用的结构

  • 混淆 - 把类名、属性名、方法名替换为晦涩难懂的1到2个字母的名字
    固然它也只能混淆Java代码,Android工程中Native代码,资源文件(图片、xml),它是没法混淆的。并且对于Java的常量值也是没法混淆的,因此不要使用常量定义平文的密码等重要信息。同时对于混淆,咱们能够经过代码制定去混淆那些,不去混淆那些。

-keep public class com.rensanning.example.Test

第5步:转化为dex

调用dx.bat将全部的class文件转化为classes.dex文件,dx会将class转换为Dalvik字节码,生成常量池,消除冗余数据等。因为dalvik是一种针对嵌入式设备而特殊设计的java虚拟机,因此dex文件与标准的class文件在结构设计上有着本质的区别,当java程序编译成class后,使用dx工具将全部的class文件整合到一个dex文件,目的是其中各个类可以共享数据,在必定程度上下降了冗余,同时也是文件结构更加经凑,实验代表,dex文件是传统jar文件大小的50%左右。class文件结构和dex文件结构比对。

Dex和Class比对

第6步:apkbuilder

打包生成APK文件。旧的apkbuilder脚本已经废弃,如今都已经经过sdklib.jar的ApkBuilder类进行打包了。输入为咱们以前生成的包含resources.arcs的.ap_文件,上一步生成的dex文件,以及其余资源如jni、.so文件。
大体步骤为
以包含resources.arcs的.ap_文件为基础,new一个ApkBuilder,设置debugMode

apkBuilder.addZipFile(f);
apkBuilder.addSourceFolder(f);
apkBuilder.addResourcesFromJar(f);
apkBuilder.addNativeLibraries(nativeFileList);
apkBuilder.sealApk(); // 关闭apk文件
generateDependencyFile(depFile, inputPaths, outputFile.getAbsolutePath());

第7步:对APK签名

对APK文件进行签名。Android系统在安装APK的时候,首先会检验APK的签名,若是发现签名文件不存在或者校验签名失败,则会拒绝安装,因此应用程序在发布以前必定要进行签名。签名信息中包含有开发者信息,在必定程度上能够防止应用被伪造。对一个APK文件签名以后,APK文件根目录下会增长META-INF目录,该目录下增长三个文件:

  • MANIFEST.MF

  • NETEASE.RSA

  • NETEASE.SF

Android系统就是根据这三个文件的内容对APK文件进行签名检验的。签名过程主要利用apksign.jar或者jarsinger.jar两个工具。将根据咱们提供的Debug和Release两个版本的Keystore进行相应的签名。

第8步:zipalign优化

Zipalign是一个Android平台上整理APK文件的工具,它首次被引入是在Android 1.6版本的SDK软件开发工具包中。它可以对打包的Android应用程序进行优化, 以使Android操做系统与应用程序之间的交互做用更有效率,这可以让应用程序和整个系统运行得更快。用Zipalign处理过的应用程序执行时间达到最低限度,当设备运行APK应用程序时占更少的RAM。

  • Zipalign如何进行优化的呢?

调用buildtoolszipalign,对签名后的APK文件进行对齐处理,使APK中全部资源文件距离文件起始偏移为4字节的整数倍,从而在经过内存映射访问APK文件时会更快。同时也减小了在设备上运行时的内存消耗。若是对于为什么提速不理解,那么能够看下内存对齐的规则以及做用该篇文章,对于内存对齐的好处有比较生动详细的解释。最终这样咱们的APK就生成完毕了。

典型的APK中内容

  • AndroidManifest.xml 程序全局配置文件

  • classes.dex Dalvik字节码

  • resources.arsc 资源索引表

  • META-INF该目录下存放的是签名信息

  • res 该目录存放资源文件

  • assets该目录能够存放一些配置或资源文件

总结

至此,对于Andoid项目构建过程的分析已经完成,固然,并没与深刻到源码层级的分析,本文的旨在对于构建过程流程上的了解和其中一些优化的缘由所在,为后续经过Gradle插件hook构建过程来作必定的操做,作一个铺垫。接下来会推出对于构建AndroidStudio上运行的Gradle插件的一系列文章。

参考文章

Android APK 签名原理及方法
 改善android性能工具篇【zipalign】
Android应用程序资源的编译和打包过程分析
Android资源管理框架(Asset Manager)简要介绍和学习计划
Android代码混淆之ProGuard

相关文章
相关标签/搜索