APK編譯流程-詳解AAPT

前文java

APK編譯流程-詳解AAPT

aapt2

aapt2(Android Asset Packageping Tool)是用來 編譯和打包 app資源文件的構建工具。aapt2 解析、索引並將資源編譯成適合Android 平臺的二進制格式.android

aap2將資源文件的編譯分爲2個步驟:git

編譯階段:
解析資源文件並生成擴展名爲.flat的中間二進制文件github

**連接階段: **
將編譯階段生成的全部中間文件(資源表、二進制XML文件和處理過的PNG文件)合併到一個APK中,此外在此資源還能夠生成其餘輔助文件,如R.java文件和ProGurad規則文件。 此時生成的APK文件並不包含DEX文件。bash

aapt只有一個編譯過程,aapt2將資源編譯分爲2個部分能夠實現資源文件的增量編譯。app

aapt2 使用示例

dump

可使用dump命令查看apk文件內的resouces信息ide

aapt2 dump <xx.apk>
複製代碼

編譯命令

compile: 編譯res文件夾下的資源文件生成 flat文件工具

aapt2 compile --dir <res-path> -o res.zip  -v
複製代碼

參數含義:
--dir :資源文件路徑,編譯整個文件夾下的資源文件
-o: 指定文件
-v: verbose logging佈局

增量編譯,及只從新編譯變化的資源文件post

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文件包含哪些內容:

image.png

一個完整的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工具打開

image.png

結合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
複製代碼

image.png

能夠看到輸出的內容和咱們在AS中看到的內容實際上是同樣的,只不過這裏是文本格式標識,結構採用縮進的方式。

總結

  1. Android開發中,咱們使用的R類 由aapt工具生成,
  2. AndroidManifest文件會被優化被轉換成二進制格式存儲
  3. aapt工具生成arsc文件是android apk資源文件的索引表
相關文章
相關標籤/搜索