APK编译流程-详解AAPT

APK编译流程-详解AAPT

aapt2

aapt2(Android Asset Packageping Tool)是用来 编译和打包 app资源文件的构建工具。aapt2 解析、索引并将资源编译成适合Android 平台的二进制格式.

aap2将资源文件的编译分为2个步骤:

编译阶段:
解析资源文件并生成扩展名为.flat的中间二进制文件

**链接阶段: **
将编译阶段生成的所有中间文件(资源表、二进制XML文件和处理过的PNG文件)合并到一个APK中,此外在此资源还可以生成其他辅助文件,如R.java文件和ProGurad规则文件。 此时生成的APK文件并不包含DEX文件。

aapt只有一个编译过程,aapt2将资源编译分为2个部分可以实现资源文件的增量编译。

aapt2 使用示例

dump

可以使用dump命令查看apk文件内的resouces信息

aapt2 dump <xx.apk>

编译命令

compile: 编译res文件夹下的资源文件生成 flat文件

aapt2 compile --dir <res-path> -o res.zip  -v

参数含义:
–dir :资源文件路径,编译整个文件夹下的资源文件
-o: 指定文件
-v: verbose logging

增量编译,及只重新编译变化的资源文件

aapt2 compile <file-path> -o <outputPath>

参数含义:
-o:因为这里是指定文件编译,所以-o 标识flat文件保存的文件夹

链接命令

link: 链接资源文件 及AndroiedManifest.xml 生成只包含资源的apk文件,并生成R类

aapt2 link res.zip \
-I ~/Library/Android/sdk/platforms/android-28/android.jar \
--java package/ \
--manifest app/src/main/AndroidManifest.xml \
-o res.apk -v

编译过程中,可能出现 style/Theme.AppCompat.Light.DarkActionBar not found.这是因为,我们引用的第三方库的资源并没有被编译,因此出现资源找不到的问题。而使用gradle 编译时,会合并所有资源统一进行编译,因此不会出现类型问题.
此时,可将对第三方资源的引用去除,改为只用使用android sdk内的资源,比如将主题改为 
<style name=“AppTheme” parent=“android:Theme.Holo.Light.DarkActionBar”>

aapt2 产物解析

aapt2编译后的apk文件包含哪些内容:

APK编译流程-详解AAPT_第1张图片

一个完整的aapt2编译后的压缩文件应该包含以下资源

1.res文件夹内的图片及xml资源(只包含图片、布局) 
2.assets文件夹
3.二进制AndroidManifest.xml
4.资源索引表 resources.arsc
5.R类文件

Demo中使用的资源不包含 assets 文件,在实际开发过程,在link 阶段,可以通过-A指定assets路径。

R类文件

在aapt的编译过程中,除了assets资源外,其他的资源(string、color、layout等)都会被赋予一个资源ID.
资源的ID常量值保存在R类文件中供在开发阶段使用。

/* AUTO-GENERATED FILE. DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found. It
 * should not be modified by hand.
 */

package com.hellobike.androidbuildintroduce;

public final class R {
  public static final class color {
    public static final int colorAccent=0x7f010000;
    public static final int colorPrimary=0x7f010001;
    public static final int colorPrimaryDark=0x7f010002;
  }
  public static final class drawable {
    public static final int ic_launcher_background=0x7f020001;
    public static final int ic_launcher_foreground=0x7f020002;
  }
  public static final class layout {
    public static final int activity_main=0x7f030000;
  }
  public static final class mipmap {
    public static final int ic_launcher=0x7f040000;
    public static final int ic_launcher_round=0x7f040001;
  }
  public static final class string {
    public static final int app_name=0x7f050000;
  }
  public static final class style {
    /**
     * Base application theme.
     */
    public static final int AppTheme=0x7f060000;
  }
}

id组成

资源Id为16进制8位 int值,组成为ppttdddd,前2位表示packageId,第3-4位表示资源类别(resouceTypeId),后4位标识该资源类别下的资源ID(resouceId)。

其中系统的packageID为[01-7f)之间,应用程序的packageId默认为7f。 typeId 没有明细的映射关系,Id的值的分配为aapt2在处理过程中 根据资源类别被处理的先后从1开始递增1分配。resouceId同理,不过是从0开始递增的

aapt2中资源ID分配的源码

resources.arsc文件详解

在开发阶段,我们可以通过aapt生成的R类中的id 引用具体的资源,而 resources.arsc文件是一个资源索引表,供在程序运行时根据id索引到具体的资源。这里我们不分析运行时索引资源的过程,只分析reousce.arsc中的内容结构。

直接去分析二进制的resources.arsc会比较困难,这里开发者可以借助AS 自带的Anaylyze Apk来分析 resouces.arsc中的内容。开发者可以直接将一个apk文件拖到AS中,AS会自动使用Anaylyze Apk工具打开

APK编译流程-详解AAPT_第2张图片

结合aapt2工具的源代码,可以看出资源索引表的内容大概包含以下部分

1.ResouceTablePackage
红色圈选的部分列出了这个资源索引表包含的package,一般一个应用只会有一个package,并且这个package的id默认为7f.

class ResourceTablePackage {
 public:
  Maybe<uint8_t> id;
  std::string name;

  std::vector<std::unique_ptr<ResourceTableType>> types;

  ResourceTablePackage() = default;
  ResourceTableType* FindType(ResourceType type);
  ResourceTableType* FindOrCreateType(const ResourceType type);

 private:
  DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
};

2.ResourceTableType
一个package关联很多resouceTypes,比如你的应用下包含了xml、stirng、layout、则这里就会有对应的type,每个type之下关联所属类型的ResouceEntry

/**
 * Represents a resource type, which holds entries defined
 * for this type.
 */
class ResourceTableType {
 public:
  /**
   * The logical type of resource (string, drawable, layout, etc.).
   */
  const ResourceType type;

  /**
   * The type ID for this resource.
   */
  Maybe<uint8_t> id;

  /**
   * Whether this type is public (and must maintain the same
   * type ID across builds).
   */
  Symbol symbol_status;

  /**
   * List of resources for this type.
   */
  std::vector<std::unique_ptr<ResourceEntry>> entries;

  explicit ResourceTableType(const ResourceType type) : type(type) {}

  ResourceEntry* FindEntry(const android::StringPiece& name);
  ResourceEntry* FindOrCreateEntry(const android::StringPiece& name);

 private:
  DISALLOW_COPY_AND_ASSIGN(ResourceTableType);
};

3.Entry
一个ResourceEntry就是一个资源项,一个资源项根据不同的配置可能会有不同的值,比如string entry可以设置多国语言,layout entry可以根据设备分辨率设置不同的布局
**

/**
 * Represents a resource entry, which may have
 * varying values for each defined configuration.
 */
class ResourceEntry {
 public:
  /**
   * The name of the resource. Immutable, as
   * this determines the order of this resource
   * when doing lookups.
   */
  const std::string name;

  /**
   * The entry ID for this resource.
   */
  Maybe<uint16_t> id;

  /**
   * Whether this resource is public (and must maintain the same entry ID across
   * builds).
   */
  Symbol symbol_status;

  /**
   * The resource's values for each configuration.
   */
  std::vector<std::unique_ptr<ResourceConfigValue>> values;

  explicit ResourceEntry(const android::StringPiece& name) : name(name.to_string()) {}

  ResourceConfigValue* FindValue(const ConfigDescription& config);
  ResourceConfigValue* FindValue(const ConfigDescription& config,
                                 const android::StringPiece& product);
  ResourceConfigValue* FindOrCreateValue(const ConfigDescription& config,
                                         const android::StringPiece& product);
  std::vector<ResourceConfigValue*> FindAllValues(const ConfigDescription& config);
  std::vector<ResourceConfigValue*> FindValuesIf(
      const std::function<bool(ResourceConfigValue*)>& f);

 private:
  DISALLOW_COPY_AND_ASSIGN(ResourceEntry);
};

4.ResouceConfigValue
reouceConfigValue即资源项具体的值。
**

class ResourceConfigValue {
 public:
  /**
   * The configuration for which this value is defined.
   */
  const ConfigDescription config;

  /**
   * The product for which this value is defined.
   */
  const std::string product;

  /**
   * The actual Value.
   */
  std::unique_ptr<Value> value;

  ResourceConfigValue(const ConfigDescription& config, const android::StringPiece& product)
      : config(config), product(product.to_string()) {}

 private:
  DISALLOW_COPY_AND_ASSIGN(ResourceConfigValue);
};

另外也可以使用aapt2命令查看resouces.arsc文件的具体内容

aapt2 dupms <xx.apk>

这里我直接将输出内容保存到txt中,并使用sublime查看

aapt2 dumps <xx.apk> >> ./outputs.txt

APK编译流程-详解AAPT_第3张图片

可以看到输出的内容和我们在AS中看到的内容其实是一样的,只不过这里是文本格式标识,结构采用缩进的方式。

总结

  1. Android开发中,我们使用的R类 由aapt工具生成,
  2. AndroidManifest文件会被优化被转换成二进制格式存储
  3. aapt工具生成arsc文件是android apk资源文件的索引表

你可能感兴趣的:(----android,apk编译过程)