AAPT命令行使用实践

环境

  • 操作系统:Mac OS High Sierra 10.13.2 (17C88)
  • Android SDK Build Tool版本:28.0.3

AAPT是什么

AAPT 即 Android Asset Packaging Tool,它是 Android SDK 自带的一个命令行工具,是 Android 构建系统的一部分。AAPT可以查看、创建以及更新兼容 ZIP 格式的归档文件(.zip、.jar、.apk),在 Android 构建流程中,我们使用它来编译和打包项目的资源文件。

可以在下面两个地方找到aapt工具:

Android SDK根目录/build-tools/xx.x.x/
AOSP/out/host/darwin-x86/bin/

AAPT命令行实践

在命令行直接输入aapt,不带任何参数,我们可以看到详细的帮助文档:

➜ ~ aapt
Android Asset Packaging Tool

Usage:
 aapt l[ist] [-v] [-a] file.{zip,jar,apk}
   List contents of Zip-compatible archive.

 aapt d[ump] [--values] [--include-meta-data] WHAT file.{apk} [asset [asset ...]]
   strings          Print the contents of the resource table string pool in the APK.
   badging          Print the label and icon for the app declared in APK.
   permissions      Print the permissions from the APK.
   resources        Print the resource table from the APK.
   configurations   Print the configurations in the APK.
   xmltree          Print the compiled xmls in the given assets.
   xmlstrings       Print the strings of the given compiled xml assets.

 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]

   Package the android resources.  It will read assets and resources that are
   supplied with the -M -A -S or raw-files-dir arguments.  The -J -P -F and -R
   options control which files are output.

 aapt r[emove] [-v] file.{zip,jar,apk} file1 [file2 ...]
   Delete specified files from Zip-compatible archive.

 aapt a[dd] [-v] file.{zip,jar,apk} file1 [file2 ...]
   Add specified files to Zip-compatible archive.

 aapt c[runch] [-v] -S resource-sources ... -C output-folder ...
   Do PNG preprocessing on one or several resource folders
   and store the results in the output folder.

 aapt s[ingleCrunch] [-v] -i input-file -o outputfile
   Do PNG preprocessing on a single file.

 aapt v[ersion]
   Print program version.

为了便于实验,我们新建一个 Android Demo 工程,并生成一个 demo.apk 文件待用。下面对 aapt 工具的命令一一进行实践(注意!!!输出太长的地方加了省略符号)。

命令 v[ersion]

打印aapt的版本号:

➜ ~ aapt v
Android Asset Packaging Tool, v0.2-5016651

瞟了一眼aapt的源码,发现它的这些命令,只要第一个字符对得上就OJBK了,不信你看:

➜  ~ aapt vOJBK
Android Asset Packaging Tool, v0.2-5016651

命令 l[ist]

查看兼容 ZIP 格式归档文件的内容,可选的命令参数如下表:

参数 作用
-v 列出归档文件内容的详细信息,类似’unzip -l -v’
-a 列出apk文件的资源索引表以及Android manifest的详细内容


我们使用上面生成的 demo.apk 来查看命令执行结果:

➜ aapt l app/build/outputs/apk/debug/demo.apk 
AndroidManifest.xml
META-INF/CERT.RSA
META-INF/CERT.SF
META-INF/MANIFEST.MF
assets/assets.txt
classes.dex
res/drawable-anydpi-v21/ic_launcher_background.xml
res/drawable-xxxhdpi-v4/ic_launcher_background.png
res/layout/activity_main.xml
res/mipmap-hdpi-v4/ic_launcher.png
......
res/raw/raw.txt
resources.arsc

带-v参数:

➜ aapt l -v app/build/outputs/apk/debug/demo.apk
Archive:  app/build/outputs/apk/debug/demo.apk
 Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name
--------  ------  ------- -----  -------      ----  ----  ------    ----
    2096  Deflate     772  63%    121428  11-30-79 00:00  ffffffffdf4f467e  AndroidManifest.xml
     773  Deflate     603  22%    195990  11-30-79 00:00  09bb8857  META-INF/CERT.RSA
    2006  Deflate     957  52%    194987  11-30-79 00:00  ffffffff89d38cd0  META-INF/CERT.SF
    1969  Deflate     938  52%    193999  11-30-79 00:00  fffffffff46ceacc  META-INF/MANIFEST.MF
      16  Stored       16   0%    112384  11-30-79 00:00  ffffffffe2d2eeaf  assets/assets.txt
  231704  Deflate  112343  52%         0  11-30-79 00:00  ffffffff8149f570  classes.dex
    5696  Deflate     982  83%    122249  11-30-79 00:00  0c9d0923  res/drawable-anydpi-v21/ic_launcher_background.xml
    4708  Stored     4708   0%    160564  11-30-79 00:00  ffffffffc0e2b4a8  res/drawable-xxxhdpi-v4/ic_launcher_background.png
     876  Deflate     389  56%    112525  11-30-79 00:00  029eb45b  res/layout/activity_main.xml
    2963  Stored     2963   0%    133287  11-30-79 00:00  78bc849d  res/mipmap-hdpi-v4/ic_launcher.png
    4905  Stored     4905   0%    125444  11-30-79 00:00  ffffffffac8a9f01  res/mipmap-hdpi-v4/ic_launcher_round.png
......
      17  Stored       17   0%    112456  11-30-79 00:00  fffffffffd969fde  res/raw/raw.txt
    8412  Stored     8412   0%    112972  11-30-79 00:00  ffffffffeb638dbe  resources.arsc
--------          -------  ---                            -------
  323429           195293  40%                            21 files

带-a参数:

➜ aapt l -a app/build/outputs/apk/debug/demo.apk
AndroidManifest.xml
META-INF/CERT.RSA
META-INF/CERT.SF
META-INF/MANIFEST.MF
assets/assets.txt
classes.dex
res/drawable-anydpi-v21/ic_launcher_background.xml
res/drawable-xxxhdpi-v4/ic_launcher_background.png
......
res/raw/raw.txt
resources.arsc

Resource table:
Package Groups (1)
Package Group 0 id=0x7f packageCount=1 name=cn.thismj.demo
  Package 0 id=0x7f name=cn.thismj.demo
    type 0 configCount=1 entryCount=57
      spec resource 0x7f010000 cn.thismj.demo:attr/barrierAllowsGoneWidgets: flags=0x00000000
      spec resource 0x7f010001 cn.thismj.demo:attr/barrierDirection: flags=0x00000000
      ......
      spec resource 0x7f010038 cn.thismj.demo:attr/layout_optimizationLevel: flags=0x00000000
      config (default):
        resource 0x7f010000 cn.thismj.demo:attr/barrierAllowsGoneWidgets: 
        resource 0x7f010001 cn.thismj.demo:attr/barrierDirection: 
        ......
        resource 0x7f010037 cn.thismj.demo:attr/layout_goneMarginTop: 
        resource 0x7f010038 cn.thismj.demo:attr/layout_optimizationLevel: 
    ......
    type 2 configCount=2 entryCount=1
      spec resource 0x7f030000 cn.thismj.demo:drawable/ic_launcher_background: flags=0x00000500
      config xxxhdpi:
        resource 0x7f030000 cn.thismj.demo:drawable/ic_launcher_background: t=0x03 d=0x00000002 (s=0x0008 r=0x00)
      config anydpi-v21:
        resource 0x7f030000 cn.thismj.demo:drawable/ic_launcher_background: t=0x03 d=0x00000001 (s=0x0008 r=0x00)
    ......
    type 8 configCount=1 entryCount=1
      spec resource 0x7f090000 cn.thismj.demo:style/AppTheme: flags=0x00000000
      config (default):
        resource 0x7f090000 cn.thismj.demo:style/AppTheme: 

Android manifest:
N: android=http://schemas.android.com/apk/res/android
  E: manifest (line=2)
    A: android:versionCode(0x0101021b)=(type 0x10)0x1
    A: android:versionName(0x0101021c)="1.0" (Raw: "1.0")
    ......
    E: uses-sdk (line=7)
      A: android:minSdkVersion(0x0101020c)=(type 0x10)0x13
      A: android:targetSdkVersion(0x01010270)=(type 0x10)0x1c
    E: application (line=11)
      A: android:theme(0x01010000)=@0x7f090000
      A: android:label(0x01010001)=@0x7f080000
      ......
      A: android:roundIcon(0x0101052c)=@0x7f060001
      E: activity (line=20)
        A: android:name(0x01010003)="cn.thismj.demo.MainActivity" (Raw: "cn.thismj.demo.MainActivity")
        E: intent-filter (line=21)
          E: action (line=22)
            A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "android.intent.action.MAIN")
          E: category (line=24)
            A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw: "android.intent.category.LAUNCHER")

命令 d[ump]

指定不同的子命令dump出对应的内容,如下表:

子命令 作用
strings 列出apk中资源表的字符串池
badging 列出apk中清单文件声明的属性标签跟应用图标
permissions 列出apk清单文件声明的权限
resources 列出apk文件的资源索引表
configurations 列出apk资源声明的所有配置
xmltree 解析并打印指定被编译的二进制xml文件的元素
xmlstrings 解析并打印指定被编译的二进制xml文件的字符串池

strings

➜ aapt d strings app/build/outputs/apk/debug/demo.apk
String pool of 15 unique UTF-8 non-sorted strings, 15 entries and 0 styles using 664 bytes:
String #0: Demo
String #1: res/drawable-anydpi-v21/ic_launcher_background.xml
String #2: res/drawable-xxxhdpi-v4/ic_launcher_background.png
String #3: res/layout/activity_main.xml
String #4: res/mipmap-hdpi-v4/ic_launcher.png
......
String #12: res/mipmap-xxxhdpi-v4/ic_launcher.png
String #13: res/mipmap-xxxhdpi-v4/ic_launcher_round.png
String #14: res/raw/raw.txt

badging

➜ aapt d badging app/build/outputs/apk/debug/demo.apk
package: name='cn.thismj.demo' versionCode='1' versionName='1.0' compileSdkVersion='28' compileSdkVersionCodename='9'
sdkVersion:'19'
targetSdkVersion:'28'
uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE'
uses-permission: name='android.permission.INTERNET'
application-label:'Demo'
application-icon-160:'res/mipmap-mdpi-v4/ic_launcher.png'
application-icon-240:'res/mipmap-hdpi-v4/ic_launcher.png'
application-icon-320:'res/mipmap-xhdpi-v4/ic_launcher.png'
application-icon-480:'res/mipmap-xxhdpi-v4/ic_launcher.png'
application-icon-640:'res/mipmap-xxxhdpi-v4/ic_launcher.png'
application-icon-65534:'res/mipmap-mdpi-v4/ic_launcher.png'
application: label='Demo' icon='res/mipmap-mdpi-v4/ic_launcher.png'
testOnly='-1'
application-debuggable
launchable-activity: name='cn.thismj.demo.MainActivity'  label='' icon=''
uses-permission: name='android.permission.READ_EXTERNAL_STORAGE'
uses-implied-permission: name='android.permission.READ_EXTERNAL_STORAGE' reason='requested WRITE_EXTERNAL_STORAGE'
feature-group: label=''
  uses-feature: name='android.hardware.faketouch'
  uses-implied-feature: name='android.hardware.faketouch' reason='default feature for all apps'
main
supports-screens: 'small' 'normal' 'large' 'xlarge'
supports-any-density: 'true'
locales: '--_--'
densities: '160' '240' '320' '480' '640' '65534'

permissions

➜ aapt d permissions app/build/outputs/apk/debug/demo.apk
package: cn.thismj.demo
uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE'
uses-permission: name='android.permission.INTERNET'

resources

➜ aapt d resources app/build/outputs/apk/debug/demo.apk
Package Groups (1)
Package Group 0 id=0x7f packageCount=1 name=cn.thismj.demo
  Package 0 id=0x7f name=cn.thismj.demo
    type 0 configCount=1 entryCount=57
      spec resource 0x7f010000 cn.thismj.demo:attr/barrierAllowsGoneWidgets: flags=0x00000000
       ......
      spec resource 0x7f010038 cn.thismj.demo:attr/layout_optimizationLevel: flags=0x00000000
      config (default):
        resource 0x7f010000 cn.thismj.demo:attr/barrierAllowsGoneWidgets: <bag>
        ......
        resource 0x7f010038 cn.thismj.demo:attr/layout_optimizationLevel: <bag>
    ......
    type 8 configCount=1 entryCount=1
      spec resource 0x7f090000 cn.thismj.demo:style/AppTheme: flags=0x00000000
      config (default):
        resource 0x7f090000 cn.thismj.demo:style/AppTheme: <bag>

configurations

➜ aapt d configurations app/build/outputs/apk/debug/demo.apk

mdpi
hdpi
xhdpi
xxhdpi
xxxhdpi
anydpi-v21

xmltree

➜ aapt d xmltree app/build/outputs/apk/debug/demo.apk res/layout/activity_main.xml
N: android=http://schemas.android.com/apk/res/android
  N: app=http://schemas.android.com/apk/res-auto
    E: android.support.constraint.ConstraintLayout (line=2)
      A: android:layout_width(0x010100f4)=(type 0x10)0xffffffff
      A: android:layout_height(0x010100f5)=(type 0x10)0xffffffff
      E: TextView (line=9)
        A: android:layout_width(0x010100f4)=(type 0x10)0xfffffffe
        A: android:layout_height(0x010100f5)=(type 0x10)0xfffffffe
        A: android:text(0x0101014f)="Hello World!" (Raw: "Hello World!")
        A: app:layout_constraintBottom_toBottomOf(0x7f01000c)=(type 0x10)0x0
        A: app:layout_constraintLeft_toLeftOf(0x7f01001f)=(type 0x10)0x0
        A: app:layout_constraintRight_toRightOf(0x7f010023)=(type 0x10)0x0
        A: app:layout_constraintTop_toTopOf(0x7f010028)=(type 0x10)0x0

xmlstrings

➜ aapt d xmlstrings app/build/outputs/apk/debug/demo.apk res/layout/activity_main.xml
String pool of 14 unique UTF-8 non-sorted strings, 14 entries and 0 styles using 436 bytes:
String #0: layout_width
String #1: layout_height
String #2: text
String #3: layout_constraintBottom_toBottomOf
String #4: layout_constraintLeft_toLeftOf
String #5: layout_constraintRight_toRightOf
String #6: layout_constraintTop_toTopOf
String #7: Hello World!
String #8: TextView
String #9: android
String #10: android.support.constraint.ConstraintLayout
String #11: app
String #12: http://schemas.android.com/apk/res-auto
String #13: http://schemas.android.com/apk/res/android

命令 p[ackage]

Android项目的构建流程就是用这个命令来编译和打包资源文件的,这个命令比较复杂,可选参数也非常多,我们列举一些比较重要的参数如下表:

参数 作用
-v 输出详细信息(通过详细信息可以粗略分析aapt打包的流程)
-f 强制覆盖已经存在的文件
-M 指定 AndroidManifest 文件的完整路径
-I 添加已经存在的资源包(一般是对应平台的系统资源包,可以添加多个)
-A 指定 assets 资源目录
-S 指定 res 资源目录(可以多个,从左到右优先级递减)
-J 指定生成 R.java 的目录
-m 在-J指定的目录下增加包名对应的目录
-F 指定生成资源包的路径(ZIP格式兼容文件)
–max-res-version 如果指定为 19 ,则类似 drawable-v21 的资源文件夹会被忽略
–no-crunch 不预处理 PNG 资源图片

一般来说,一个Android项目会依赖很多Module和第三方库,这些依赖库也会包含自己的资源文件,所以在进行编译和打包资源文件之前,首先需要对资源进行合并。例如,对上面的Demo工程而言,如果我们想打一个debug的版本,那么Android Studio的Gradle插件在编译打包资源文件之前会先把所有资源合并到如下目录:

//res
app(主工程)/build/intermediates/res/merged/debug  

//AndroidManifest文件
app(主工程)/build/intermediates/manifests/full/debug/AndroidManifest.xml

p命令比较复杂,我们可以用Demo工程来做一个简单的试验,首先尝试用p命令生成R.java文件:

➜ aapt p \
-M app/build/intermediates/manifests/full/debug/AndroidManifest.xml \
-I ~/Library/Android/sdk/platforms/android-28/android.jar \
-A app/src/main/assets \
-S app/build/intermediates/res/merged/debug/ \
-J output \
-m \
-f

此时查看Demo工程目录output文件夹下生成的文件:

➜ tree output 
output
└── cn
    └── thismj
        └── demo
            └── R.java

3 directories, 1 file

继续用p命令编译打包生成资源包文件:

➜ aapt p \
-M app/build/intermediates/manifests/full/debug/AndroidManifest.xml \
-I ~/Library/Android/sdk/platforms/android-28/android.jar \
-A app/src/main/assets \
-S app/build/intermediates/res/merged/debug/ \
-F output/resource-debug.ap_ \
-f

再次查看output文件夹:

➜ tree output
output
├── cn
│   └── thismj
│       └── demo
│           └── R.java
└── resource-debug.ap_

3 directories, 2 files

我们把生成的资源文件包resource-debug.ap_解压,查看里面的文件结构:

➜  resource-debug tree
.
├── AndroidManifest.xml
├── assets
│   └── assets.txt
├── res
│   ├── drawable-anydpi-v21
│   │   └── ic_launcher_background.xml
│   ├── drawable-hdpi-v4
│   │   └── ic_launcher_background.png
│   ├── drawable-ldpi-v4
│   │   └── ic_launcher_background.png
│   ├── drawable-mdpi-v4
│   │   └── ic_launcher_background.png
│   ├── drawable-xhdpi-v4
│   │   └── ic_launcher_background.png
│   ├── drawable-xxhdpi-v4
│   │   └── ic_launcher_background.png
│   ├── drawable-xxxhdpi-v4
│   │   └── ic_launcher_background.png
│   ├── layout
│   │   └── activity_main.xml
│   ├── mipmap-hdpi-v4
│   │   ├── ic_launcher.png
│   │   └── ic_launcher_round.png
│   ├── mipmap-mdpi-v4
│   │   ├── ic_launcher.png
│   │   └── ic_launcher_round.png
│   ├── mipmap-xhdpi-v4
│   │   ├── ic_launcher.png
│   │   └── ic_launcher_round.png
│   ├── mipmap-xxhdpi-v4
│   │   ├── ic_launcher.png
│   │   └── ic_launcher_round.png
│   ├── mipmap-xxxhdpi-v4
│   │   ├── ic_launcher.png
│   │   └── ic_launcher_round.png
│   └── raw
│       └── raw.txt
└── resources.arsc

16 directories, 22 files

命令 r[emove]

删除(.zip、.jar、.apk等)里面指定的文件,可选参数:

参数 作用
-v 输出详细信息(好像没什么用)

删除上述p命令生成的资源包(resource-debug.ap_)中的某些文件:

➜ aapt r -v output/resource-debug.ap_ res/layout/activity_main.xml AndroidManifest.xml 

命令 a[dd]

添加文件到(.zip、.jar、.apk等),可选参数:

参数 作用
-v 输出详细信息(没什么用)

添加一些文件到上述p命令生成的资源包(resource-debug.ap_)中。

➜ a apt a -v output/resource-debug.ap_ classes.dex Demo.iml 
 'classes.dex'...
 'Demo.iml'...

命令 c[runch]

对一个或多个资源文件夹下的PNG图片进行预处理(内部使用 libpng 库来解析 PNG 文件,用 zlib 库压缩生成新图片),该命令需要指定参数如下:

参数 作用
-S 指定源文件夹
-C 指定源文件预处理完成之后保存的文件夹

在 Mac 系统下随便截了一张图,放在 Demo 工程的 png 目录,然后尝试用 AAPT 进行预处理,结果保存在 Demo 工程的 output/result 目录,结果如下:

➜ aapt c -v -S png -C output/result
Crunching PNG Files in source dir: png
To destination dir: output/result
Processing image to cache: png/截图.png => output/result/截图.png
  (processed image to cache entry output/result/截图.png: 67% size of source)
Crunched 1 PNG files to update cache

命令 s[ingleCrunch]

仅仅对单张 PNG 图片进行预处理,该命令需要指定参数如下:

参数 作用
-i PNG源文件路径
-o PNG源文件预处理完成之后保存的路径

把刚才的截图用 s 命令再处理一遍:

➜ aapt s -v -i png/截图.png -o output/result.png
Crunching single PNG file: png/截图.png
    Output file: output/result.png
Processing image to cache: png/截图.png => output/result.png
  (processed image to cache entry output/result.png: 67% size of source)

又去瞟了一眼源码,发现还有这么个奇淫巧技:

➜ aapt m
Ready
s
png/截图.png
output/xx.png      
Crunching png/截图.png
Crunching single PNG file: png/截图.png
    Output file: oxx.png
Done
quit

总结

通过以上的实践,算是对 AAPT 有了一个初步的了解,知道了 AAPT 在整个 Android 项目构建流程中的主要职责。实际上,在 Android Gradle Plugin 3.0.0以上版本,已经默认使用 AAPT2 了,AAPT2 为资源的增量编译提供了支持……

参考文档

  1. Android aapt
  2. 关于Android图片资源瘦身的奇思妙想

  转载请注明: ThisMJ AAPT命令行使用实践

 上一篇
AAPT源码分析(一) AAPT源码分析(一)
前言通过 AAPT命令行使用实践,我们已经知道了如何去使用 aapt 这个工具。为了更详细地了解 aapt 的工作流程以及原理,我们接下来从源码的角度来分析一下,它是如何编译和打包项目资源文件的。aapt 相关源码的位置如下所示: AOSP
2019-03-15
下一篇 
浅析ZIP格式 浅析ZIP格式
ZIP文件格式是一种数据压缩和文档储存的文件格式,原名Deflate,发明者为菲尔·卡茨(Phil Katz),他于1989年1月公布了该格式的资料。ZIP通常使用后缀名“.zip”,它的MIME格式为application/zip。
2019-02-14
  目录