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文件包含哪些内容:
一个完整的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开始递增的
resources.arsc文件详解
在开发阶段,我们可以通过aapt生成的R类中的id 引用具体的资源,而 resources.arsc文件是一个资源索引表,供在程序运行时根据id索引到具体的资源。这里我们不分析运行时索引资源的过程,只分析reousce.arsc中的内容结构。
直接去分析二进制的resources.arsc会比较困难,这里开发者可以借助AS 自带的Anaylyze Apk来分析 resouces.arsc中的内容。开发者可以直接将一个apk文件拖到AS中,AS会自动使用Anaylyze Apk工具打开
结合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>
可以看到输出的内容和我们在AS中看到的内容其实是一样的,只不过这里是文本格式标识,结构采用缩进的方式。
总结
- Android开发中,我们使用的R类 由aapt工具生成
- AndroidManifest文件会被优化被转换成二进制格式存储
- aapt工具生成arsc文件是android apk资源文件的索引表
附:
在我们打包apk时候会生成2个比较重要的跟资源有关系的:
1>:R.java文件,就是一个类,里边放一些 id,比如我们调用 getDrawable(),需要传递一个id;
2>:resource.arsc文件:是一个资源的映射信息,比如说有一个 ResTable,表示头信息、线程池信息、字符串的池信息;
比如说我需要加载一张图片,叫做 image_src,在低分辨率会找 drawable-xdpi,稍微高分辨率会找 drawable-xxdpi等等,我该怎样可以找到drawable-xdpi,怎样可以找到 drawable-xxdpi,对应的去找哪个分辨率,是根据 resource.arsc文件有关系的,它会去解析 resource.arsc文件
所以我们要想找到一些资源,必须有两个东西进行配合,就是R.java文件和 resource.arsc文件。