精华内容
下载资源
问答
  • Android打包流程

    2019-10-08 12:44:35
    Android打包流程 图为典型的Android应用模块的编译流程 Android编译流程的步骤: 打包资源文件Resource Files,生成R.java文件(AAPT:Android Asset Packaging Tool) 处理AIDL文件,生成java代码(AIDL:Android...

    Android打包流程

    在这里插入图片描述
    图为典型的Android应用模块的编译流程
    Android编译流程的步骤:

    1. 打包资源文件Resource Files,生成R.java文件(AAPT:Android Asset Packaging Tool)
    2. 处理AIDL文件,生成java代码(AIDL:Android Interface Definition Language,实现进程间的通信)
    3. 编译代码文件(Source code),生成对应的.class文件
    4. .class文件转化为dex文件
    5. 将dex文件和编译的资源文件组合成成apk(apk-builder)
    6. 使用调试或发布密钥库签名(jarsigner)
    7. 应用优化,减少运行时占用的内存(zipslign:归档对其工具 —> mmap内存映射 —> 减少运行时消耗的内存)

    ps:

    • 如果您使用的是 apksigner,则只能在为 APK 文件签名之前执行 zipalign。如果您在使用 apksigner 为 APK 签名之后对 APK 进行进一步更改,则签名将会失效。
    • 如果您使用的是 jarsigner,则只能在为 APK 文件签名之后执行 zipalign。

    ps2:

    • 我们经常直接解压一些apk的时候会发现有很多的classes.dex文件,默认的dex文件应该是一个的,但是每个dex文件的方法数不能超过65535。我们只要在glide下配置multiDexEnabled true就好了
      某解压后的apk
    展开全文
  • Android 打包流程

    2020-04-04 21:27:44
    Android 打包流程 Ⅰ 起因 本来想好好琢磨琢磨写个开头,后来一想,做技术的哪来这么多讲究,直接开门见山。 和一些同行交流的过程中发现部分朋友实际上对 Android 打包流程基本没有比较清晰完整的概念,所以本文...

    Android 打包流程
    Ⅰ 起因

    本来想好好琢磨琢磨写个开头,后来一想,做技术的哪来这么多讲究,直接开门见山。 

    和一些同行交流的过程中发现部分朋友实际上对 Android 打包流程基本没有比较清晰完整的概念,所以本文主要聊一聊 Android 的打包流程,以及目前已有的或多或少可以与 Android 打包搭一点边的开源技术,互相学习。 

    更好的了解 Android 的打包流程,有助于我们做一些针对编译期的优化,包括但不限于提高打包速度(毕竟时间就是金钱,我的朋友),提高 Android 应用的 hack 难度增加安全性,减少 Android 安装包的体积,完成一些自动化的编码减少编码压力等。 

    以上这些在本文中,都会有简要的提及。希望可以对大家有一点帮助。 

    Ⅱ Apk 的组成结构

    本文脉络采用的是一个由结果反推至过程的流程。 在开始时,我们需要先弄明白,apk 文件也就是 Android 打包流程的产物,是一个什么类型的文件,它包含了一些什么内容。 弄清楚了这些,我们就可以带着目的性,去分析打包流程,可以更好的关注 apk 文件中的这些内容是在打包流程的哪个过程中产生,以及是如何产生的。

    众所周知,apk 文件本质上其实是一个 zip 包。想要知道其中包含了什么,改后缀直接解压便可以看到了。 这里笔者挑软柿子,解压了某个未经过加固或者其他手段加密的 Android 安装包文件,以下为结果截图:

    主要注意红色标注部分,这一些文件和文件夹是一个 Android 应用基本都具备的。而其他的一些文件和文件夹则是一些第三方库,或者是其他一些代码生成的。 接下来,依次大概介绍一下这些文件和文件夹的作用。

    2.1. AndroidManifest.xml

    这是 Android 应用的全局配置文件,它包含了这个应用的很多配置信息,例如包名、版本号、所需权限、注册的服务等。可以根据这个文件在相当程度上了解这个应用的一些信息。该文件目前状态是被编译为二进制的 XML 文件,可以通过一些工具(如 apktool)反编译后进行查看。 

    这里强烈安利大家升级一下 Android Studio 2.2 Preview,其新功能 Analyze apk是一个很不错的 apk 分析(~~hack 入门~~)工具。我们可以通过它直接反编译看到原始的 AndroidManifest.xml 文件,如下图。 

    2.2. assets 文件夹

    assets 文件夹用于保存需要保持原始文件的资源文件夹,开发过程中拖了什么到里面,打包完之后里面还是什么。一般用于存放音频,网页(帮助页面之类的),字体等文件。主要需要知道的点是,它与 res 文件夹的区分以及如何在应用中访问该文件夹的资源,如它可以有多级目录而 res 则只有两级。 

    2.3. dex 文件

    classes.dex 文件是 Android 系统运行于 Dalvik Virtual Machine 上的可执行文件,也是Android 应用程序的核心所在。项目工程中的 Java 源码通过 javac 生成 class 文件,再通过 dx 工具转换为 classes.dex,注意到我们这里有 classes2.dex 和 classes3.dex。这是方法数超过一个 dex 的上限,分 dex 的结果。分 dex 在 Android 5.0 之前需要开发者自行完成,5.0 后 dx 自带支持。dex 文件的数据结构不算复杂,如下图所示。目前一些热补丁有关的技术,主要便是对 dex 做各种处理。 

    2.4. lib 文件夹

    该目录存放着应用需要的 native 库文件。比如一些底层实现的图片处理、音视频处理、数据加密的库以 so 库的形式在该文件夹中。而该文件夹下有时会多一个层级,这是根据不同CPU 型号而划分的,如 ARM,ARM-v7a,x86等,如下图: 

    2.5. META-INF 文件夹

    该目录的主要作用是用于保证 APK 的完整性以及安全性。该文件夹下,主要有三个文件。如下图: 

    先说 MANIFEST.MF,这个文件保存了整个 apk 文件中所有文件的文件名 + SHA-1后的 base64 编码值。这也就意味着,MANIFEST.MF 象征着 apk 包的完整性。再说 CERT.RSA,这个文件保存了公钥和加密方式的信息。最后说 CERT.SF,这个文件与 MANIFEST.MF 的结构一样,只是其编码会被被私钥加密。这样一来每次安装时,通过该文件夹中的文件,就可以完成验证的过程。如果 apk 包被改变了,而篡改者没有私钥生成 CERT.SF,则无法完成校验。 

    2.6. res 文件夹

    顾名思义,该文件夹是资源文件夹。它里面存放的所有文件都会被映射到 R 文件中,生成对应的资源 ID,便于代码中通过 ID 直接访问。其中的资源文件包括了动画(anim),图像(drwable),布局(layout),常量值(values),颜色值(colors),尺寸值(dimens),字符串(strings),自定义样式(styles)等。 

    2.7. resource.arsc 文件

    这个文件可以说是所有文件中结构最复杂的。
    它记录了资源文件,资源文件位置(各个维度的路径)和资源 id 的映射关系。并且将所有的 string 都存放在了 string pool 中,节省了在查找资源时,字符串处理的开销。
    我们可以使用 Androdi Studio 2.2 Preview 中的新功能 Analyze apk (这个新功能用来分析 apk 非常好用,强烈推荐各位读者可以尝试一下)来看看它到底包含了些什么,一图胜过千言: 

    我们可以看到,首先是有个 package 可选,实际上 resource.arsc 是可以包含多个 package 的资源的。 然后可以看到一个 Resource Types 的列表。这里看到的是 drawable 的 type。 右边显示了有多少个 drawable 以及多少项 configurations,以及表的具体内容为 ID - Name - 各个维度的值(在这里即是资源的路径),通过这个,我们可以完成通过 id + 对应的 configuration 获取对应资源的操作。
    而后面要提到资源混淆的原理,就是修改这里各个维度的值,并修改对应 res 里面的文件夹以及文件名实现的。
    具体其完整的数据结构比较复杂,在这里就不展开说了,有兴趣的读者可以自行查阅信息,甚至写一个 parser 出来也是非常有意思的。 

    Ⅲ Android 打包流程

    在了解了 apk 文件的结构后,我们开始分析 Android 的打包流程。 

    3.1 资源

    Android 打包流程的第一步,是处理资源文件。
    在这个步骤中,起主要作用的是 aapt。
    刚刚提及的 AndroidManifest.xml, res 文件夹,resource.arsc 文件的生成都与其有关,简单来说,aapt 解析项目代码中的 AndroidManifest.xml,收集项目中 res 文件夹的资源文件及 xml 文件,对其做压缩以及编译的处理。在此过程中,分配了资源 id 并生成了 R.java 文件 以及 arsc 文件。 

    3.2 代码

    上一步得到了 R.java 文件后,将其与项目代码一起编译得到 .class文件,然后打包为 jar 包。这个过程中,还会有混淆代码这一步骤。之后,再通过 dx 工具,将生成的 jar 包与第三方库的 jar 包一起编译为 dex 文件。这个过程中,如果是 5.0 以前的系统且超过了 65535 方法数的限制,需要人为的分 dex,5.0 以后则由 dx 工具包办。 

    到这一步,实际上 apk 所需要的主要内容已经大致齐全了。只需要把上面生成的 AndroidManifest.xml,classes.dex,res文件夹,resource.arsc 打包进 apk,并且将项目工程中的 assets 以及 lib 目录一并放入,就有了一个未经签名的 Android 安装包了。 

    3.3 签名

    接下来还缺简单但是却关键的最后一步,那便是 apk 包的签名,这一步在之前对 META-INF 的介绍中,实际已有提及。只需要按步骤生成 MANIFEST.MF, CERT.RSA,CERT.SF 并放入META-INF 文件夹即可。 

    以上便是 Android 打包的基本流程,宏观来看实际并不复杂,但是其中的一些步骤展开来讲,却是很有内容的,比如 appt 对资源处理的那个部分,R.java 是如何生成的,resource.arsc 又是如何生成的,Android 是怎样完成对资源的获取的,这些内容笔者之后会继续整理分享。 

    Ⅳ 业内有关技术小结

    分析完流程之后,我们来看看目前业内有哪一些与编译有关的技术可以运用起来的。 

    4.1 apk 加固

    目前业内已经有很多 apk 加固的服务,这里提及最常见的一种加固方案。
    一个比较典型的加固流程如下图: 

    实际上是通过外包一层解壳 apk,将我们自己的 dex 文藏在加密 apk 的 dex 中。由于破坏了正常的 dex 结构,所以一般的反编译工具,如 apktool 并不能直接反编译 apk。但是如果了解了加密的方式以及方案,实际上要破解得到脱壳 dex 并不复杂,这只是一定程度上提高了 hack 的成本,不过针对一般的 hack 依然很有效。 

    4.2 快速多渠道包

    由于国内有着数不胜数的 Android 应用市场,所以越来越多的渠道包成为了每一个应用的必须。
    在之前,开发者一般通过 AndroidManifest.xml 中的meta 信息来区分渠道。在了解了打包流程后,大家应该明白,一旦改变了 AndroidManifest.xml 就意味着要重新打包。
    ts(多渠道包的时间) = t (打每个包的耗时) * n (n为渠道数量), n 在大到一定程度后,ts 就会变得非常的大。这显然是让开发者十分痛苦的。
    要解决这个问题,实际上需要攻破的是,META-INF 的完整性校验机制。
    目前业界比较常用的两种方案是:
    1. META-INF 下添加空文件不会破坏签名(文件名为渠道号,若 Google 更改签名机制,有可能失效)
    2. apk 文件末尾写入信息(本质是利用了 zip 文件可以添加 comment 数据结构的特点) 

    这两种改动方案都不会导致 MANIFEST.MF 文件的改变,也就不需要再次打包,只需要简单的读改文件即可。为广大开发者节省了上线前漫长的等待渠道包的时间。 

    4.3 资源混淆

    资源混淆通过混淆资源路径名以及资源文件名,比如: 

    res/drawable/icon -> r/s/a

    这样不但可以减少安装包的体积,一定程度上也可以提高破解难度。
    该方案目前业内也有两种实现,但是原理基本一致。入手点皆为resource.arsc。
    第一种方案是修改我们刚刚提及的 aapt,使得在生成 resource.arsc 的过程中,就修改掉项目中资源的名称,实现了资源的混淆。
    第二种方案则是在打出 apk 包之后,读入已生成的 resource.arsc 文件,进行混淆,改写,同时修改掉 res 文件夹下的资源名称,完成混淆。最后再重新打包得到混淆资源后的新 apk 文件。 

    既可以减小体积,又可以一定程度增加被 hack 的难度,如果还有没有用起来的同学,可以尝试用起来了。 

    4.4 热补丁

    由于移动平台的特性,移动应用版本的升级率并不高。这种时候,如果应用应用也可以像网页那般动态部署,无疑可以带来非常大的优势,比如在线修复 bug,小版本的升级,临时特性的上线等。
    目前业界已经有比较多的技术方案了,在这里我们提及两种比较有代表性的方案。
    1. 通过 native hook 的方式,替换 class 中的方法实现完成热补丁。
    2. classloader 加载新 dex 覆盖原有 class 完成替换的方案。
    因为实际上每种方案都并非几句话可以讲解清楚,本文主要目的也只是做个大概介绍,若读者有兴趣可以谷歌后进一步研究,这里就不具体展开讲了。 

    实际上还有一些技术,比如使用 Anotation 自动生成代码,buck exopackage 提高打包速度等。有兴趣的话,可以自行查阅,相信可以学到不少知识的。 

    展开全文
  • android 打包流程

    2016-12-28 09:06:29
    http://www.cnblogs.com/niray/p/5242985.html
    http://www.cnblogs.com/niray/p/5242985.html
    
    展开全文
  • Android 打包流程之aapt打包资源文件

    千次阅读 2019-02-27 11:33:44
    Android应用最终是以apk的形式放在手机上安装并运行的,而负责将资源文件和代码进行打包的工具就叫appt,全称Android Asset Packaging Tool,翻译过来就是Android资源打包工具,是Android打包流程中不可或缺的一环。...

    上一篇:Android打包流程之资源管理
    Android应用最终是以apk的形式放在手机上安装并运行的,而负责将资源文件和代码进行打包的工具就叫appt,全称Android Asset Packaging Tool,翻译过来就是Android资源打包工具,是Android打包流程中不可或缺的一环。虽然build-tools中都会有一个aapt.exe负责打包apk,但底层还是通过执行aapt命令的方式来进行操作,所以这里需要了解一下aapt的相关命令,有助于更好的理解打包的流程。

    Android打包流程简述

    先来看张官方的打包流程图:


    Android打包流程图

    1. 首先将资源文件(res目录下)通过aapt工具打包成R.java类(资源索引)和.arsc资源文件。
    2. 倘若工程中又aidl,则通过aidl工具将aidl打包成java接口类。
    3. R.java、aidl生成的java文件和项目中的源代码混合编译成.class文件
    4. class文件和第三方jar或者library通过dx工具打包成dex文件。dx工具的主要作用是将java字节码转换成Dalvik字节码,在此过程中会压缩常量池,消除一些冗余信息等。
    5. 通过apkbuilder工具将所有没有编译的资源,.arsc资源,.dex文件打包到一个完成apk文件中。
    6. 生成的apk通过Jarsinger工具,利用relese或debug下配置的keystore文件进行签名,得到签名后的apk文件。
    7. 通过zipAlign工具对签名后的apk进行对齐处理。即将APK包中所有的资源文件距离文件起始偏移为4字节整数倍,这样通过内存映射访问apk文件时的速度会更快。减少运行时内存的使用。

    这就是Android打包成apk的流程,而资源的打包则涉及到许多aapt命令。

    AAPT命令详解

    1、aapt l[ist] [-v] [-a] file.{zip,jar,apk}
    列出(zip、jar、apk)等类型压缩包中的所有文件列表,示例:

    aapt list  app-debug.apk
    
    #list可以简写为l,如下:
    aapt l  app-debug.apk
    

    列出app-debug.apk中所有的文件列表:


    在这里插入图片描述

    打印出来的内容很长,在命令行中不方便观察,我们可以将输出的内容存放到.txt文件中便于查看。示例:

    aapt list  app-debug.apk>a.txt
    

    把输出的内容存入a.txt文件中。
    aapt list后面还可以加-v和-a。例如加-v参数:

    aapt list -v app-debug.apk>a.txt
    

    也会列出apk中所有的文件列表,只不过更加详细:
    在这里插入图片描述

    其中各字段代表的含义如下:
    Length:文件的长度。
    Method:数据压缩算法,有Deflate和Stored两种。知道是用来压缩文件的就行。
    Ratio:压缩率。
    Size:文件压缩后节省的大小。跟压缩率有关。Size=(1-压缩率)*Length。
    Date:日期。
    Time:时间。
    CRC-32:循环冗余校验,是一种加密算法。
    Name:文件名称。

    aapt list后面加-a参数:

    aapt list -a app-debug.apk>a.txt
    

    列出apk中所有的文件列表、资源id、各限定符下资源文件对应的id、AndroidManifest文件内容。


    在这里插入图片描述

    看下右边的滚动条,发现这个输出的内容不要太多,不要太详细。最底下列出了Android Manifest文件的内容。

    2、aapt d[ump] [–values] [–include-meta-data] WHAT file.{apk} [asset [asset …]]
    通过参数配置列出apk中各种详细信息,例如apk的权限、字符、资源等等。
    主要有以下几种参数:

    1. strings
      列出apk中的所有字符资源(包括不同语言限定符):示例:
    aapt dump strings app-debug.apk>a.txt
    

    结果:


    在这里插入图片描述

    这里有乱码,有其它国家语言符号。大家可以自行试一试。

    1. badging
      Print the label and icon for the app declared in APK。实际内容不止label和icon。还有包名、versionCode、versionName、compileSdkVersion、targetSdkVersion等等信息,这里截取一小部分:


      在这里插入图片描述

    2. permissions
      列出apk所用到的所有权限。示例:

    aapt dump permissions app-debug.apk
    

    结果会列出apk所需要的所有权限,这里不贴图了。

    1. resources。
      输出apk中所有的资源信息,包括用户添加的资源、不同限定符下的系统资源等等,示例:
    aapt dump resources app-debug.apk>a.txt
    

    结果:


    在这里插入图片描述

    1. configurations
      列出apk中所有的资源目录,注意,只是目录,不包含任何文件内容,资源目录对应不同的限定符。
    aapt dump configurations app-debug.apk
    

    关于限定符这篇文章有写到:限定符,结果如下:


    在这里插入图片描述

    5.xmltree
    打印出指定xml文件的文档树结构。例如打印出项目中res/layout/activity_main.xml的树形结构,则可以这样写:

    aapt d xmltree app-debug.apk res/layout/activity_main.xml
    

    打印结果如下:


    在这里插入图片描述

    从上到下列出了activity_main.xml的文档树结构。

    6.xmlstrings
    列出给出的xml文件中所包含的控件名、控件的属性名和属性值等等。示例:

    aapt d xmlstrings app-debug.apk res/layout/activity_main.xml
    

    结果:


    在这里插入图片描述

    3、aapt p[ackage]
    负责打包资源文件的命令,这个命令是aapt中最核心、最复杂的命令:

    aapt p[ackage] [-d][-f][-m][-u][-v][-x][-z][-M AndroidManifest.xml] \
            [-0 extension [-0 extension ...]] [-g tolerance] [-j jarfile] \
            [--debug-mode] [--min-sdk-version VAL] [--target-sdk-version VAL] \
            [--app-version VAL] [--app-version-name TEXT] [--custom-package VAL] \
            [--rename-manifest-package PACKAGE] \
            [--rename-instrumentation-target-package PACKAGE] \
            [--utf16] [--auto-add-overlay] \
            [--max-res-version VAL] \
            [-I base-package [-I base-package ...]] \
            [-A asset-source-dir]  [-G class-list-file] [-P public-definitions-file] \
            [-D main-dex-class-list-file] \
            [-S resource-sources [-S resource-sources ...]] \
            [-F apk-file] [-J R-file-dir] \
            [--product product1,product2,...] \
            [-c CONFIGS] [--preferred-density DENSITY] \
            [--split CONFIGS [--split CONFIGS]] \
            [--feature-of package [--feature-after package]] \
            [raw-files-dir [raw-files-dir] ...] \
            [--output-text-symbols DIR]
    

    好吧,来看下每个参数啥意义:

    -d:包含一个或多个设备资源,由逗号分割。
    -f:强制覆盖现有文件。
    -m:生成包的目录在-j参数指定的目录。
    -u:更新现有的包。
    -v:详细输出,加上此命令会在控制台输出每一个资源文件信息,R.java生成后还有注释。
    -x:创建拓展资源id。
    -z:需要本地化的资源属性标记定位。
    -M:指定AndroidManifest文件的路径。
    -0:指定一个附加扩展名,对于该扩展名,此类文件将不会压缩存储在.apk中。空字符串表示不压缩所有文件。
    -g:强制图片灰度,灰度值默认为0-jar:指定要包含的类的jar或zip文件。
    --debug-mode:设置调试模式,即在AndroidManifest中加入  android:debuggable="true"
    --min-sdk-version VAL:指定最小SDK版本 如是7以上 则默认编译资源的格式是 utf-8
    --target-sdk-version VAL:AndroidManifest中指定目标编译版本sdk--app-version VAL:指定app的版本号。
    --app-version-name TEXT:指定app的版本名。
    --custom-package VAL:生成R.java到不同的包。
    --rename-manifest-package PACKAGE:修改manifest中的应用包名。
    --rename-instrumentation-target-package PACKAGE:重写指定包名的选项
    --utf16:将资源的默认编码更改为UTF-16。在api在7或者更高时有用,资源的默认编码是utf-8--auto-add-overlay:自动添加资源覆盖。
    --max-res-version VAL:最大资源版本,忽略高于给定值的版本化资源目录。
    -I base-package:指定的SDK版本中android.jar的路径。
    -A asset-source-dir:指定Assets文件夹路径。
    -G class-list-file:指定用于输出混淆选项的文件。
    -P public-definitions-file:指定的输出公共资源,可以指定一个文件 让资源ID输出到上面。
    -D main-dex-class-list-file:指定主DEX的混淆选项输出的文件。
    -S resource-sources:指定资源目录,一般是res。
    -F apk-file:指定把资源输出到 apk文件中。
    -J R-file-dir指定R.java的输出路径。
    -c CONFIGS:指定资源有哪些限定符,中间以逗号分割,如en、port、land、en_US。
    --preferred-density DENSITY:指定设备的屏幕密度,不符合此密度的资源将会被删除。
    --split CONFIGS:分包构建apk,可以和主apk一起加载。
    --output-text-symbols DIR:在指定的文件夹下生成一个包含R.java中的资源标识符的文本文件。
    

    这命令复杂的分分钟让人抓狂-。-
    但其实只要知道这个命令是做什么的就好,它就是用来将项目中的所有资源文件打包,经过我的实践,其最小执行单元如下:

    aapt package -S [res文件夹路径] -M [AndroidManifest.xml文件路径] -I [sdk中android.jar的路径] -F [xxx.apk]
    

    意思就是最起码要指定这四个值,才能执行aapt package命令。于是我这里执行了一个命令。

    aapt package -S res -M E:\AndroidProjects\Dagger2\app\src\main\AndroidManifest.xml -I D:\Android\android-sdk-windows\platforms\android-28\android.jar -F out.apk
    

    结果:


    在这里插入图片描述

    然后就报了这个错误,说是资源找不到,可用as打开工程,这些资源明明都能找到。一度陷入绝望,后来实在没办法,把这些资源删了,AndroidManifest.xml文件中删除这一行代码:android:theme="@style/TheTheme",于是才可以执行成功。至于加上这三个资源、指定主题为什么会报错,我在网上找了许多资料都没有找到这个问题的症结所在,若是有哪位大神知道这个问题的解决办法,还请在底下留言告诉我,不胜感激~我真的很想知道他喵的到底是为什么-。-

    好了,至于打包后就是一个out.apk压缩文件,我们解压后,目录如下:
    在这里插入图片描述

    res文件夹里面是我们的资源目录:


    在这里插入图片描述

    至于resources.arsc就是资源打成的包了,将资源文件打包成.arsc的文件,就是我们上面流程图的第一步。以后打包apk的时候会将这个文件也一同打包进去。还有一个AndroidManifest.xml,打开后一堆看不懂的:
    在这里插入图片描述

    这里的文件内容做了处理,防止别人窥探到其中的代码。至于其它可以接的参数,这里只演示一个:

    --rename-manifest-package PACKAGE:修改manifest中的应用包名。
    

    好吧,然后我们在原来的命令上接入该参数:

    aapt package -S res -M E:\AndroidProjects\Dagger2\app\src\main\AndroidManifest.xml -I D:\Android\android-sdk-windows\platforms\android-28\android.jar  -F out.apk --rename-manifest-package com.aapt.demo
    

    在刚才命令的最后面加入–rename-manifest-package com.aapt.demo,把AndroidManifest.xml中的包名改成com.aapt.demo,打包完成后解压并查看AndroidManifest文件:


    在这里插入图片描述

    虽然大部分是乱码,可是我们还是能看到包名已经改成了com.aapt.demo。至于接其它的参数,笔者这里没有一个个试,其实常用的就那些,其他的都是辅助参数。

    4、aapt r[emove] [-v] file.{zip,jar,apk} file1 [file2 …]
    用于移除打包好的apk中的文件。例如移除打包好的apk中的AndroidManifest.xml文件:

    aapt r out.apk AndroidManifest.xml
    

    这里移除了out.apk中的AndroidManifest.xml文件,然后执行如下命令:

    aapt d xmmtree out.apk AndroidManifest.xml
    

    然后会出现如下错误:


    在这里插入图片描述

    提示该文件找不到,可见确实文件是被删除了。可是这里奇怪的是,解压apk,发现AndroidManifest.xml文件依旧在,但执行命令的时候就提示找不到。什么原因,暂时还不知道,有知道的可以告诉我。

    5、 aapt a[dd] [-v] file.{zip,jar,apk} file1 [file2 …]
    添加文件到打包好的apk中。示例:将a.txt、b.txt添加到打包好的apk中:

    aapt a out.apk a.txt b.txt
    

    多个文件中间以空格分割就好,然后看下解压apk,看其中的目录:


    在这里插入图片描述

    将a.txt和b.txt成功加入到apk中了。

    6、 aapt c[runch] [-v] -S resource-sources … -C output-folder …
    做PNG文件的预处理,并将结果存储到一个文件夹中。示例,把res目录下的图片预处理,并存储到任意路径pictures中:

    aapt c -S res -C E:\AndroidProjects\Dagger2\app\src\main\pictures
    

    执行后结果如下:


    在这里插入图片描述

    表示执行成功,然后我们看下pictures路径中的文件:


    在这里插入图片描述

    好吧,没什么可说的。

    7、aapt s[ingleCrunch] [-v] -i input-file -o outputfile
    对单个PNG文件进行预处理,并输出到指定文件:

    aapt s -v -i[需要处理的图片文件路径] -o[处理完成后存储的图片文件路径]
    

    示例:

    aapt s -v -i E:\AndroidProjects\Dagger2\app\src\main\res\mipmap-hdpi\ic_launcher.png -o E:\AndroidProjects\Dagger2\app\src\main\pictures\a.png
    

    这里预先在pictures文件夹中新建a.txt文件,然后把后缀名改成.png,这时候a.png是无法打开的,执行此命令后,a.png就可以打开了吗,显示的图像就是res中的hdpi目录下的ic_launcher.png。

    8、 aapt v[ersion]
    没什么好说的,显示aapt的版本


    在这里插入图片描述

    aapt的命令差不多就这么几种。在命令行中执行aapt命令,就可以查看aapt详细的用法。有兴趣的小伙伴可以自行试一试哈~

    AAPT源码分析

    关于源码,这里只是看一个脉络。需要下载Android系统源码并解压,下载地址:各版本系统源码下载。我这里下载的是6.0的源码。关于aapt部分的源码在frameworks\base\tools\aapt文件夹下。其入口在Main.cpp中main方法中。main方法共有近500行,这里只贴一部分来分析:

    int main(int argc, char* const argv[])
    {
        char *prog = argv[0];
    	//Bundle对象用来存储输入的操作类型和相关的参数。
        Bundle bundle;
        bool wantUsage = false;
        int result = 1;    // pessimistically assume an error.
        int tolerance = 0;
    
        /* default to compression */
        bundle.setCompressionMethod(ZipEntry::kCompressDeflated);
    
        if (argc < 2) {
            wantUsage = true;
            goto bail;
        }
    
    	....
    	//argv[] 一行aapt命令被分割成字符串数组。
    	//这边判断该数组的第二个元素的第1个字符,如aapt package,argv[1][0]获取到的就是p。
        else if (argv[1][0] == 'p')
    		//设置执行类型为打包。
            bundle.setCommand(kCommandPackage);
    	
    	....
        
        argc -= 2;
        argv += 2;
    
        /*
         * Pull out flags.  We support "-fv" and "-f -v".
         */
        while (argc && argv[0][0] == '-') {
            /* flag(s) found */
            const char* cp = argv[0] +1;
    
            while (*cp != '\0') {
                switch (*cp) {
           
    			......
    		    //收集appt命令输入的参数,这些参数以"-"开头。
                case '-':
                    if (strcmp(cp, "-debug-mode") == 0) {
    					//例如这里就获取到了-debug-mode,然后设置Bundle的debugMode为true。
                        bundle.setDebugMode(true);
                    } 
    				.....
    				
                    cp += strlen(cp) - 1;
                    break;
                default:
                    fprintf(stderr, "ERROR: Unknown flag '-%c'\n", *cp);
                    wantUsage = true;
                    goto bail;
                }
    
                cp++;
            }
            argc--;
            argv++;
        }
    
        /*
         * We're past the flags.  The rest all goes straight in.
         */
        bundle.setFileSpec(argv, argc);
    
    	//执行最终的命令,并得到结果
        result = handleCommand(&bundle);
    
    bail:
        if (wantUsage) {
            usage();
            result = 2;
        }
    
        //printf("--> returning %d\n", result);
        return result;
    }
    
    

    源码中比较详细,这里举个例子。整行aapt命令会被分割成字符串数组。然后去字符串数组的第二个的第一个字符,如aapt package…,得到p,代表这是aapt打包命令。Bundle对象用以存储输入的操作类型和相关的参数,供后面执行命令详细操作时使用。当然,命令解析错误时,会通过goto跳转到bail代码块,比如:

     default:
         fprintf(stderr, "ERROR: Unknown flag '-%c'\n", *cp);
         wantUsage = true;
         goto bail;
    

    bail代码块:

    bail:
        if (wantUsage) {
            usage();
            result = 2;
        }
        //printf("--> returning %d\n", result);
        return result;
    }
    

    好吧,会调用usage()函数,这个函数会打印出aapt的用法文档。

    void usage(void)
    {
        fprintf(stderr, "Android Asset Packaging Tool\n\n");
        fprintf(stderr, "Usage:\n");
        fprintf(stderr,
            " %s l[ist] [-v] [-a] file.{zip,jar,apk}\n"
            "   List contents of Zip-compatible archive.\n\n", gProgName);
        fprintf(stderr,
            " %s d[ump] [--values] [--include-meta-data] WHAT file.{apk} [asset [asset ...]]\n"
            "   strings          Print the contents of the resource table string pool in the APK.\n"
            "   badging          Print the label and icon for the app declared in APK.\n"
            "   permissions      Print the permissions from the APK.\n"
            "   resources        Print the resource table from the APK.\n"
            "   configurations   Print the configurations in the APK.\n"
            "   xmltree          Print the compiled xmls in the given assets.\n"
            "   xmlstrings       Print the strings of the given compiled xml assets.\n\n", gProgName);
        fprintf(stderr,
            " %s p[ackage] [-d][-f][-m][-u][-v][-x][-z][-M AndroidManifest.xml] \\\n"
            "        [-0 extension [-0 extension ...]] [-g tolerance] [-j jarfile] \\\n"
            "        [--debug-mode] [--min-sdk-version VAL] [--target-sdk-version VAL] \\\n"
            "        [--app-version VAL] [--app-version-name TEXT] [--custom-package VAL] \\\n"
            "        [--rename-manifest-package PACKAGE] \\\n"
            "        [--rename-instrumentation-target-package PACKAGE] \\\n"
            "        [--utf16] [--auto-add-overlay] \\\n"
            "        [--max-res-version VAL] \\\n"
            "        [-I base-package [-I base-package ...]] \\\n"
            "        [-A asset-source-dir]  [-G class-list-file] [-P public-definitions-file] \\\n"
            "        [-S resource-sources [-S resource-sources ...]] \\\n"
            "        [-F apk-file] [-J R-file-dir] \\\n"
            "        [--product product1,product2,...] \\\n"
            "        [-c CONFIGS] [--preferred-density DENSITY] \\\n"
            "        [--split CONFIGS [--split CONFIGS]] \\\n"
            "        [--feature-of package [--feature-after package]] \\\n"
            "        [raw-files-dir [raw-files-dir] ...] \\\n"
            "        [--output-text-symbols DIR]\n"
    

    这是代码,看下我们执行aapt时的输出:


    在这里插入图片描述

    是不是就是这个?好吧,当命令校验成功,会执行handleCommand方法,并传入一个Bundle对象。看下handleCommand方法的代码:

    /*
     * Dispatch the command.
     */
    int handleCommand(Bundle* bundle)
    {
        //printf("--- command %d (verbose=%d force=%d):\n",
        //    bundle->getCommand(), bundle->getVerbose(), bundle->getForce());
        //for (int i = 0; i < bundle->getFileSpecCount(); i++)
        //    printf("  %d: '%s'\n", i, bundle->getFileSpecEntry(i));
    
        switch (bundle->getCommand()) {
        case kCommandVersion:      return doVersion(bundle);
        case kCommandList:         return doList(bundle);
        case kCommandDump:         return doDump(bundle);
        case kCommandAdd:          return doAdd(bundle);
        case kCommandRemove:       return doRemove(bundle);
        case kCommandPackage:      return doPackage(bundle);
        case kCommandCrunch:       return doCrunch(bundle);
        case kCommandSingleCrunch: return doSingleCrunch(bundle);
        case kCommandDaemon:       return runInDaemonMode(bundle);
        default:
            fprintf(stderr, "%s: requested command not yet supported\n", gProgName);
            return 1;
        }
    }
    

    也没什么,就判断是哪种命令,比如package、dump、add、remove,然后再去做具体的操作,如doPackage、doDump、doRemove等等。不过这些方法是通过外部引用的,其真正的代码在同目录下的Command.cpp文件中,这个c文件实现了所有的aapt命令的具体代码。这里举一个稍微简单的例子doRemove看一下:

    /*
     * Delete files from an existing archive.
     */
    int doRemove(Bundle* bundle)
    {
    	//命令行举例:aapt r out.apk AndroidManifest.xml
        ZipFile* zip = NULL;
        status_t result = UNKNOWN_ERROR;
        const char* zipFileName;
    
        if (bundle->getFileSpecCount() < 1) {
    		//如果没有指定压缩包名称,提示必须指定压缩包名称
            fprintf(stderr, "ERROR: must specify zip file name\n");
            goto bail;
        }
    	//获取到压缩文件名称。[out.apk,AndroidManifest.xml]数组中的第一个。
        zipFileName = bundle->getFileSpecEntry(0);
    
    	//[out.apk,AndroidManifest.xml]数组大小小于2,说明没有要移除的文件。
        if (bundle->getFileSpecCount() < 2) {
            fprintf(stderr, "NOTE: nothing to do\n");
            goto bail;
        }
    
    	//类似于java中的打开输入输出流。
        zip = openReadWrite(zipFileName, false);
        if (zip == NULL) {
    		//打开文件失败。
            fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
                zipFileName);
            goto bail;
        }
    
    	//索引从1开始,可以有多个要删除的文件。
        for (int i = 1; i < bundle->getFileSpecCount(); i++) {
    		//获取要删除的文件名。
            const char* fileName = bundle->getFileSpecEntry(i);
            ZipEntry* entry;
    
    		//获取要删除的文件。
            entry = zip->getEntryByName(fileName);
            if (entry == NULL) {
    			//文件找不到
                printf(" '%s' NOT FOUND\n", fileName);
                continue;
            }
    
    		//找到该文件则删除该文件。
            result = zip->remove(entry);
    
            if (result != NO_ERROR) {
                fprintf(stderr, "Unable to delete '%s' from '%s'\n",
                    bundle->getFileSpecEntry(i), zipFileName);
                goto bail;
            }
        }
    
    	//相当于java中输入输出流的刷新。
        zip->flush();
    
    bail:
        delete zip;
        return (result != NO_ERROR);
    }
    

    首先会判断命令中有没有指定压缩包,如果没有,会报错。有指定压缩包才会去获取命令中压缩包的名称。如果没有指定要删除的文件,会提示没有指定要删除的文件。否则打开文件,在打开文件成功的情况下,循环遍历要删除的文件,依次把该文件从压缩包中删除,全部操作完成后刷新压缩包中的内容。

    以上就是执行aapt remove命令底层所执行的详细代码了,说到底aapt所有的命令最底层都是在操作文件。只是google把这些复杂的文件操作封装成命令工具供我们使用。这里的源码分析也只是看一个大体的结构,更具体的实现,各位小伙伴有兴趣可以去翻一翻源码啊,不一定要精通c++,有一点基础或者其它语言基础也能看得懂大概的流程,除非涉及到修改然后重新编译成定制化的aapt命令,那又是另外一回事了。

    好了,关于aapt命令就写到这里了,知道怎么用,底层大概是怎么实现的就可以了,有深厚c++功底的同学可以自己尝试改一改。文章中若是有不足或错误,看到的小伙伴可以在底下留言告诉我,小菜鸟不胜感激~

    展开全文
  • 本文会带你深入了解安卓打包流程的各个细节。更重要的是,熟悉安卓打包流程会让你对apk瘦身、参数化构建、资源文件处理有更深的理解。
  • 参考: Android应用程序(APK)的编译打包过程
  • Ionic5项目android打包流程1、apk打包2、生成jks(Java Key Store)文件3、生成签名的apk文件4、apk文件优化与重命名 ionic开发好应用以后,需要先进行打包、签名、优化,才能在各大应用商店中进行发布,本文就apk的...
  • Android打包流程之资源管理

    千次阅读 2019-02-21 11:04:33
    前言:前天某个公众号突然给我推了一篇文章,里面给出了Android打包编译知识的思维导图,于是觉得作为Android开发者有必要了解一下这个流程。即使现在的知识储备不够,但是人嘛,总是在学习中不断成长的。一步一步,...
  • 安卓打包流程-nodejs

    2020-06-08 17:29:37
    1.安装nodejs 2.nodejs命令行执行: npm install -g ionic cordova 版本查看指令: node --version npm --version nodejs,使用淘宝源 ...Angular 环境搭建 ...安装Android Studio 一、环境 1.ionic 5,angular 8
  • 1.首先打开android studio 按照下图依次选择android/app/java/java/MainApplicaion 然后修改 MainApplication.java的 @Override public boolean getUseDeveloperSupport() { return ture;} 改为return false;//...
  • Android 打包流程: 官网地址:http://developer.android.com/tools/building/index.html 具体的打包步骤如下: 1:生成R.java类文件:Eclipse中会自动生成R.java,ant和命令行使用android SDK提供...
  • UE4_Android打包流程注意及问题总结

    千次阅读 2018-09-14 11:57:12
    UE4安卓开发 ue4 安卓建项及打包_实例教程(多图长帖) ...UE4|安卓打包流程及常见问题 转载:https://blog.csdn.net/dingd_158/article/details/51274995 UE4_减小安卓apk打包体积 地址:https://blog...
  • D:\Program\Android\'Android Studio'\jre\bin\keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key 路径中间有空格也会报错的,用下面这个就可以了.
  • ======================================================原文链接https://juejin.im/entry/58b78d1b61ff4b006cd47e5b,如侵删======================================================我们平时在开发...Android Stu...
  • 浅谈Android打包流程

    2019-05-24 10:23:13
    我们在 Gradle 中点击下 build 或者通过命令行 gradlew tasks,AndroidStudio 就会开始执行构建流程,最终输出APK文件。 这件事我经常干,也习以为常了,但是有时也会偶尔想想,那一串串代码是如何变成 apk 的呢,...
  • 安卓打包流程注意:安卓jdk,sdk,并配好环境变量 用户变量和系统变量的区别: 用户变量添加后改变的是当前用户有效果。系统变量是所有用户都会有效果 .sdk系统变量 Jdk系统变量 JAVA_HOME C:\Program ...
  • Ionic3--Android打包流程

    2019-02-12 13:17:36
    一、在项目文件目录运行终端输入打包命令 ionic cordova build android --prod --release
  • 1、打包安卓包首先要安卓Java的JDK,以及配置好环境变量   下载JDK的链接见下: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 根据自己电脑的配置下载对应的JDK版本,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,614
精华内容 14,245
关键字:

安卓打包流程