APK资源文件的生成
2017-11-27 本文已影响0人
我叫王菜鸟
详细链接在我的CSDN上:
http://blog.csdn.net/wangwei708846696/article/details/78614287
参数
aapt p -f -m -J mygen/ -S res/ -I ~/tool/android-sdk-linux/platforms/android-17/android.jar -A assets/
-M AndroidManifest.xml -F helloworldresources.apk
上面这些参数会对执行过程产生影响,我们就针对这个参数进行最基本的打包分析
总体过程
int doPackage(Bundle* bundle)
{
sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
err = configFilter->parse(bundle->getConfigurations());//当制定参数c的时候可以得到
outputAPKFile = bundle->getOutputAPKFile();//我们在参数F指定
// 检查该文件是否存在不存在则创建,并确定其实常规文件
if (outputAPKFile) {
FileType type;
type = getFileType(outputAPKFile);
if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
fprintf(stderr,"ERROR: output file '%s' exists but is not regular file\n",outputAPKFile);
goto bail;
}
}
//创建AaptAssets对象,这个对象用于收集当前需要编译的资源文件。
//这些需要编译的资源文件就保存在AaptAssets类的成员变量mRes中
assets = new AaptAssets();
//将AndroidManifest.xml文件目录assets和res下的资源目录和资源文件收录起来保存到AaptAssets中的成员变量中
err = assets->slurpFromArgs(bundle);
//将收集的文件最终写入apk
builder = new ApkBuilder(configFilter);
if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
//编译res目录下资源文件以及AndroidManifest.xml文件
err = buildResources(bundle, assets, builder);
if (err != 0) {
goto bail;
}
}
//写入R文件
err = writeResourceSymbols(bundle, assets, assets->getPackage(), true, bundle->getBuildSharedLibrary());
//写入资源文件
err = writeProguardFile(bundle, assets);
//收集资源构建apk生成器,然后
err = addResourcesToBuilder(assets, builder);
//将每一份拆分写入apk
err = writeAPK(bundle, outputPath, split);
}
上面就是整个apk打包的过程,当然我着重资源文件,用图叙述上面的部分就是
TIM截图20171127102443.pngassets->slurpFromArgs(bundle);
将AndroidManifest.xml文件目录assets和res下的资源目录和资源文件收录起来保存到AaptAssets中的成员变量中
ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
{
// 获取res目录的路径,S指定的路径也就是res/
const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
sp<AaptAssets> current = this;
//获取bundle内所保存的aapt的命令选项个数,即要完成的功能个数
const int N = bundle->getFileSpecCount();
if (bundle->getAndroidManifestFile() != NULL) {
//得到AndroidManifestFile文件路径
String8 srcFile(bundle->getAndroidManifestFile());
//将这个路径添加到AaptFile中
addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
NULL, String8());
}
//获取目录名称,用A指定也就是assets/ 目录
const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
//创建一个名为”assets”的AaptDir对象
sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));//assets
//收录目录“assets”下的资源文件,并返回资源文件个路径&返回个数
count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,String8(), mFullAssetPaths, true);
//得到res目录
const char *res = resDirs[i];
//收录res下文件
count = current->slurpResourceTree(bundle, String8(res));
//下面就是收录其他文件
...
}
这里面添加AndroidManifestFile路径如何添加呢?
- 将路径添加到AaptFile中-->将AaptFile添加到AaptGroup中-->将AaptGroup添加到AaptAssets中
这块逻辑就很简单了,就是获取路径然后将路径保存在AaptAssets中主要分成三类:
- 加AndroidManifestFile文件
- res目录下的所有文件
- assets下的所偶有文件
buildResources(bundle, assets, builder);
编译res目录下资源文件以及AndroidManifest.xml文件
status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder)
{
//得到AndroidManifest路径
sp<AaptGroup> androidManifestFile = assets->getFiles().valueFor(String8("AndroidManifest.xml"));
//解析AndroidManifest.xml文件
status_t err = parsePackage(bundle, assets, androidManifestFile);
ResourceTable::PackageType packageType = ResourceTable::App;//默认是app
if (bundle->getBuildSharedLibrary()) {//指定-shared-lib参数
packageType = ResourceTable::SharedLibrary;
} else if (bundle->getExtending()) {//指定x参数
packageType = ResourceTable::System;
} else if (!bundle->getFeatureOfPackage().isEmpty()) {
packageType = ResourceTable::AppFeature;//指定-feature-of
}
//根据包名创建一个对应的ResourceTable ,在上面解析AndroidManifest.xml中解析的包名
ResourceTable table(bundle, String16(assets->getPackage()), packageType);
//添加被引用资源包,比如系统的那些android:命名空间下的资源也就是android.jar
err = table.addIncludedResources(bundle, assets);
//设置编译XML文件的选项为标准和UTF-8的编码方式
int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
//生成一个关于key-value的集合用于收集资源文件
KeyedVector<String8, sp<ResourceTypeSet> > *resources = new KeyedVector<String8, sp<ResourceTypeSet> >;
//调用collect_files将前面收集到assets中的各类资源文件重新收集到resources中来
collect_files(assets, resources);
//定义收集各类资源文件的容器
sp<ResourceTypeSet> drawables;
sp<ResourceTypeSet> layouts;
sp<ResourceTypeSet> anims;
sp<ResourceTypeSet> animators;
sp<ResourceTypeSet> interpolators;
sp<ResourceTypeSet> transitions;
sp<ResourceTypeSet> xmls;
sp<ResourceTypeSet> raws;
sp<ResourceTypeSet> colors;
sp<ResourceTypeSet> menus;
sp<ResourceTypeSet> mipmaps;
//将保存到resources中的各类文件分类到每个容器中
ASSIGN_IT(drawable);
ASSIGN_IT(layout);
ASSIGN_IT(anim);
ASSIGN_IT(animator);
ASSIGN_IT(interpolator);
ASSIGN_IT(transition);
ASSIGN_IT(xml);
ASSIGN_IT(raw);
ASSIGN_IT(color);
ASSIGN_IT(menu);
ASSIGN_IT(mipmap);
//设置assets的资源为resources中保存的
assets->setResources(resources);
//这段也就是我们的overlay机制,如果大家有兴趣可以研究下,就是一个链表,进行获取最新的资源
//判断当前应用程序是否有overlay的资源,有的话将assets中保存的资源设置为overlay中
sp<AaptAssets> current = assets->getOverlay();
while(current.get()) {
KeyedVector<String8, sp<ResourceTypeSet> > *resources =
new KeyedVector<String8, sp<ResourceTypeSet> >;
current->setResources(resources);
collect_files(current, resources);
current = current->getOverlay();
}
// apply the overlay files to the base set
//如果有overlay资源则使用overlay资源替换现有资源
if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
!applyFileOverlay(bundle, assets, &layouts, "layout") ||
!applyFileOverlay(bundle, assets, &anims, "anim") ||
!applyFileOverlay(bundle, assets, &animators, "animator") ||
!applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
!applyFileOverlay(bundle, assets, &transitions, "transition") ||
!applyFileOverlay(bundle, assets, &xmls, "xml") ||
!applyFileOverlay(bundle, assets, &raws, "raw") ||
!applyFileOverlay(bundle, assets, &colors, "color") ||
!applyFileOverlay(bundle, assets, &menus, "menu") ||
!applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
return UNKNOWN_ERROR;
}
if (drawables != NULL) {
if (bundle->getOutputAPKFile() != NULL) {
//预处理图像, 目前只支持处理png格式图像
err = preProcessImages(bundle, assets, drawables, "drawable");
}
if (err == NO_ERROR) {
//处理drawables中的资源
//我们分析如何将收集到一个AaptAsset中的资源文件信息分类重新由函数makeFileResources组织到一个ResourceTable对象
//中去,这些资源文件的信息最终组织在Package, Type, Entry, Item中,Package代表当前编译APK的包信息,
//Type类保存资源类型信息, Entry代表保存资源文件,Item保存文件中属性信息. Package包含Type, Type包含Entry,
//Entry包含Item.
err = makeFileResources(bundle, assets, &table, drawables, "drawable");
if (err != NO_ERROR) {
hasErrors = true;
}
} else {
hasErrors = true;
}
}
//处理mipmaps类型的文件
if (mipmaps != NULL) {
if (bundle->getOutputAPKFile() != NULL) {
err = preProcessImages(bundle, assets, mipmaps, "mipmap");
}
if (err == NO_ERROR) {
err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
if (err != NO_ERROR) {
hasErrors = true;
}
} else {
hasErrors = true;
}
}
//处理layouts类型的文件
if (layouts != NULL) {
err = makeFileResources(bundle, assets, &table, layouts, "layout");
if (err != NO_ERROR) {
hasErrors = true;
}
}
...
//直到处理完所有类型的文件
//单独处理value生成id
current = assets;
while(current.get()) {
KeyedVector<String8, sp<ResourceTypeSet> > *resources = current->getResources();
ssize_t index = resources->indexOfKey(String8("values"));
if (index >= 0) {
ResourceDirIterator it(resources->valueAt(index), String8("values"));
ssize_t res;
while ((res=it.next()) == NO_ERROR) {
sp<AaptFile> file = it.getFile();
//对于values则是由这个独立的函数进行组织的,将解析完的数据保存在变量table中
res = compileResourceFile(bundle, assets, file, it.getParams(), (current!=assets), &table);
}
}
current = current->getOverlay();
}
//同样对于其他文件夹下的文件进行生成id的操作
if (colors != NULL) {
err = makeFileResources(bundle, assets, &table, colors, "color");
if (err != NO_ERROR) {
hasErrors = true;
}
}
if (menus != NULL) {
err = makeFileResources(bundle, assets, &table, menus, "menu");
if (err != NO_ERROR) {
hasErrors = true;
}
}
//下面我们就开始分配Bag资源ID
// 调用ResourceTable类的成员函数assignResourceIds分配bag资源ID信息
if (table.hasResources()) {
err = table.assignResourceIds();
if (err < NO_ERROR) {
return err;
}
}
// 最后我们将要编译XML文件,这样我们就能引用资源
if (layouts != NULL) {
ResourceDirIterator it(layouts, String8("layout"));
while ((err=it.next()) == NO_ERROR) {
String8 src = it.getFile()->getPrintableSource();
//对于对于anim, animator, interpolator, xml, color, menu, drawable中的xml文件都是通过compileXmlFile函数进行编译的.
//在这里面用XMLNode::assignResourceIds里面给每个属性赋值
err = compileXmlFile(bundle, assets, String16(it.getBaseName()),it.getFile(), &table, xmlFlags);
if (err == NO_ERROR) {
ResXMLTree block;
//将编译后的信息组织到ResXMLTree中去
block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
//检验分配的ID是否正确
checkForIds(src, block);
} else {
hasErrors = true;
}
}
if (err < NO_ERROR) {
hasErrors = true;
}
err = NO_ERROR;
}
....
//下面这些代码是产生经过flatten的AndroidManifest.xml文件,这个主要是因为在之前才生成一些bag id等
// 取出AndroidManifest.xml文件
const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
String8 manifestPath(manifestFile->getPrintableSource());
//清空manifestFile所指向的AndroidManfiest.xml的信息,然后重新解析
manifestFile->clearData();
sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
if (manifestTree == NULL) {
return UNKNOWN_ERROR;
}
//检测是否AndroidManifest.xml中是否有overlay资源,如果有就将现有资源替换
err = massageManifest(bundle, manifestTree);
if (err < NO_ERROR) {
return err;
}
//编译AndroidManifest.xml文件
err = compileXmlFile(bundle, assets, String16(), manifestTree, manifestFile, &table);
if (err < NO_ERROR) {
return err;
}
if (table.modifyForCompat(bundle) != NO_ERROR) {
return UNKNOWN_ERROR;
}
//生成资源符号表
sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"),AaptGroupEntry(), String8());
//ResourceTable::flatten用于生成资源索引表resources.arsc
err = table.flatten(bundle, split->getResourceFilter(),flattenedTable, split->isBase());
...
}
这个函数主要做什么事情呢?
TIM截图20171127113213.png我们就写一个重点就好,不用全写全写的话之前博客写过读者反映效果很差。
现在生成Bag ID:
status_t ResourceTable::assignResourceIds()
{
const size_t N = mOrderedPackages.size();
size_t pi;
status_t firstError = NO_ERROR;
// First generate all bag attributes and assign indices.
// 首先取出当前编译应用程序资源所依赖的的包个数,并分别为包中的资源分配资源ID,
//在这里这两个包分别是: android.jar 和 com.example.helloworldactivity.
for (pi=0; pi<N; pi++) {
sp<Package> p = mOrderedPackages.itemAt(pi);
if (p == NULL || p->getTypes().size() == 0) {
continue;
}
if (mPackageType == System) {
p->movePrivateAttrs();
}
/* 如果为Package对象p中的Type设定了public属性id,那么调用
** applyPublicTypeOrder函数将p中成员变量mOrderedTypes中的Type按照id
** 由小到大的顺序排列
**
** 例如, 我们在values/public.xml中如下定义:
** <?xml version="1.0" encoding="utf-8"?>
** <resources>
<public type="string" name="show" id="0x7f030001" />
<public type="style" name="AppTheme" id="0x7f040001" />
** </resources>
** 那么type为string和style的在mOrderedTypes中的位置是在2,3
** 位置处,就是将3和4进行减1操作而,第0,1两个位置保留.
*/
status_t err = p->applyPublicTypeOrder();
if (err != NO_ERROR && firstError == NO_ERROR) {
firstError = err;
}
// Generate attributes...
//按照Type-->ConfigList-->Entry的顺序依次将所有的Entry调用函数 generateAttributes生成一个属性信息
const size_t N = p->getOrderedTypes().size();
size_t ti;
for (ti=0; ti<N; ti++) {
sp<Type> t = p->getOrderedTypes().itemAt(ti);
if (t == NULL) {
continue;
}
const size_t N = t->getOrderedConfigs().size();
for (size_t ci=0; ci<N; ci++) {
sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
if (c == NULL) {
continue;
}
const size_t N = c->getEntries().size();
for (size_t ei=0; ei<N; ei++) {
sp<Entry> e = c->getEntries().valueAt(ei);
if (e == NULL) {
continue;
}
//generateAttributes函数用于将保存到mBag中的信息取出,如果
//其是一个id属性,并且在table中没有对应的bag或者entry则
//创建一个entry添加进table中
status_t err = e->generateAttributes(this, p->getName());
if (err != NO_ERROR && firstError == NO_ERROR) {
firstError = err;
}
}
}
}
uint32_t typeIdOffset = 0;
if (mPackageType == AppFeature && p->getName() == mAssetsPackage) {
typeIdOffset = mTypeIdOffset;
}
const SourcePos unknown(String8("????"), 0);
sp<Type> attr = p->getType(String16("attr"), unknown);
// Assign indices...
const size_t typeCount = p->getOrderedTypes().size();
for (size_t ti = 0; ti < typeCount; ti++) {
sp<Type> t = p->getOrderedTypes().itemAt(ti);
if (t == NULL) {
continue;
}
// 类似的,我们如果为某类Type对象指定了public的IDS信息,我们就同上
// 将Type中的ConfigList对象按照id值从小到大排列在mOrderedConfigs中去
err = t->applyPublicEntryOrder();
if (err != NO_ERROR && firstError == NO_ERROR) {
firstError = err;
}
const size_t N = t->getOrderedConfigs().size();
t->setIndex(ti + 1 + typeIdOffset);
LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
"First type is not attr!");
for (size_t ei=0; ei<N; ei++) {
sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
if (c == NULL) {
continue;
}
c->setEntryIndex(ei);
}
}
//分配bags id
for (size_t ti = 0; ti < typeCount; ti++) {
sp<Type> t = p->getOrderedTypes().itemAt(ti);
if (t == NULL) {
continue;
}
const size_t N = t->getOrderedConfigs().size();
for (size_t ci=0; ci<N; ci++) {
sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
if (c == NULL) {
continue;
}
const size_t N = c->getEntries().size();
for (size_t ei=0; ei<N; ei++) {
sp<Entry> e = c->getEntries().valueAt(ei);
if (e == NULL) {
continue;
}
// 逐个取出每一个资源属性调用Entry的assignResourceIds为其分配属性ID
//对应ResourceTable::Entry::assignResourceIds
status_t err = e->assignResourceIds(this, p->getName());
if (err != NO_ERROR && firstError == NO_ERROR) {
firstError = err;
}
}
}
}
}
return firstError;
}
按照Type-->ConfigList-->Entry的顺序依次将所有的Entry调用函数 generateAttributes生成一个属性信息
通过调用Entry的generateAttributes函数分配id
分配id的过程是如图:
image中间 02 所在位置值代表资源ID对应的资源的类型,分别是:
- 02:drawable
- 03:layout
- 04:values
- 05:xml
- 06:raw
- 07:color
- 08:menu
第二个核心就是对应生成那些类似于layout文件中的id了,因为里面牵扯到版本
status_t compileXmlFile(const Bundle* bundle,
const sp<AaptAssets>& assets,
const String16& resourceName,
const sp<XMLNode>& root,
const sp<AaptFile>& target,
ResourceTable* table,
int options)
{
// 首先去除空格
if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) {
root->removeWhitespace(true, NULL);
} else if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) {
root->removeWhitespace(false, NULL);
}
//设定编码格式
if ((options&XML_COMPILE_UTF8) != 0) {
root->setUTF8(true);
}
bool hasErrors = false;
/* 如果尚未对解析到root数据结构中的属性分配资源ID则调用
** root的成员函数分配资源id, 给属性分配资源ID原理类似于上
** 述给Bag资源分配ID */
if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) {
status_t err = root->assignResourceIds(assets, table);
if (err != NO_ERROR) {
hasErrors = true;
}
}
/* parseValues函数用于获取当前资源属性所在的行号等信息将其保存到table
** 中,并将字符串资源信息替换成对应的类型值 */
status_t err = root->parseValues(assets, table);
if (err != NO_ERROR) {
hasErrors = true;
}
if (hasErrors) {
return UNKNOWN_ERROR;
}
if (table->modifyForCompat(bundle, resourceName, target, root) != NO_ERROR) {
return UNKNOWN_ERROR;
}
if (kIsDebug) {
printf("Input XML Resource:\n");
root->print();
}
err = root->flatten(target,
(options&XML_COMPILE_STRIP_COMMENTS) != 0,
(options&XML_COMPILE_STRIP_RAW_VALUES) != 0);
if (err != NO_ERROR) {
return err;
}
if (kIsDebug) {
printf("Output XML Resource:\n");
ResXMLTree tree;
tree.setTo(target->getData(), target->getSize());
printXMLBlock(&tree);
}
target->setCompressionMethod(ZipEntry::kCompressDeflated);
return err;
}
这个过程也比较简单,就不画图了
- 将id替换之后的那些文件进行版本分类
status_t ResourceTable::modifyForCompat(const Bundle* bundle,
const String16& resourceName,
const sp<AaptFile>& target,
const sp<XMLNode>& root) {
const String16 vector16("vector");
const String16 animatedVector16("animated-vector");
const int minSdk = getMinSdkVersion(bundle);
if (minSdk >= SDK_LOLLIPOP_MR1) {
// Lollipop MR1 and up handles public attributes differently, no
// need to do any compat modifications.
return NO_ERROR;
}
const ConfigDescription config(target->getGroupEntry().toParams());
if (target->getResourceType() == "" || config.sdkVersion >= SDK_LOLLIPOP_MR1) {
// Skip resources that have no type (AndroidManifest.xml) or are already version qualified
// with v21 or higher.
return NO_ERROR;
}
sp<XMLNode> newRoot = NULL;
int sdkVersionToGenerate = SDK_LOLLIPOP_MR1;
Vector<sp<XMLNode> > nodesToVisit;
nodesToVisit.push(root);
while (!nodesToVisit.isEmpty()) {
sp<XMLNode> node = nodesToVisit.top();
nodesToVisit.pop();
if (bundle->getNoVersionVectors() && (node->getElementName() == vector16 ||
node->getElementName() == animatedVector16)) {
// We were told not to version vector tags, so skip the children here.
continue;
}
const Vector<XMLNode::attribute_entry>& attrs = node->getAttributes();//得到元素的属性列表
for (size_t i = 0; i < attrs.size(); i++) {
const XMLNode::attribute_entry& attr = attrs[i];
const int sdkLevel = getPublicAttributeSdkLevel(attr.nameResId);
if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) {
if (newRoot == NULL) {
newRoot = root->clone();
}
// Find the smallest sdk version that we need to synthesize for
// and do that one. Subsequent versions will be processed on
// the next pass.
sdkVersionToGenerate = std::min(sdkLevel, sdkVersionToGenerate);
//详情打印
if (bundle->getVerbose()) {
SourcePos(node->getFilename(), node->getStartLineNumber()).printf(
"removing attribute %s%s%s from <%s>",
String8(attr.ns).string(),
(attr.ns.size() == 0 ? "" : ":"),
String8(attr.name).string(),
String8(node->getElementName()).string());
}
node->removeAttribute(i);
i--;
}
}
// Schedule a visit to the children.
const Vector<sp<XMLNode> >& children = node->getChildren();
const size_t childCount = children.size();
for (size_t i = 0; i < childCount; i++) {
nodesToVisit.push(children[i]);
}
}
if (newRoot == NULL) {
return NO_ERROR;
}
// Look to see if we already have an overriding v21 configuration.
sp<ConfigList> cl = getConfigList(String16(mAssets->getPackage()),
String16(target->getResourceType()), resourceName);
//应该生成与版本对应的Resource资源文件
if (shouldGenerateVersionedResource(cl, config, sdkVersionToGenerate)) {
// We don't have an overriding entry for v21, so we must duplicate this one.
ConfigDescription newConfig(config);
newConfig.sdkVersion = sdkVersionToGenerate;
//每一个文件都是用AaptFile来描述比如main.xml
sp<AaptFile> newFile = new AaptFile(target->getSourceFile(),
AaptGroupEntry(newConfig), target->getResourceType());
String8 resPath = String8::format("res/%s/%s",
newFile->getGroupEntry().toDirName(target->getResourceType()).string(),
target->getSourceFile().getPathLeaf().string());
resPath.convertToResPath();
// Add a resource table entry.
if (bundle->getVerbose()) {
SourcePos(target->getSourceFile(), -1).printf(
"using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
newConfig.sdkVersion,
mAssets->getPackage().string(),
newFile->getResourceType().string(),
String8(resourceName).string(),
newConfig.toString().string());
}
//每一个资源都用Entry表示比如icon.png
addEntry(SourcePos(),
String16(mAssets->getPackage()),
String16(target->getResourceType()),
resourceName,
String16(resPath),
NULL,
&newConfig);
// Schedule this to be compiled.
CompileResourceWorkItem item;
item.resourceName = resourceName;
item.resPath = resPath;
item.file = newFile;
mWorkQueue.push(item);
}
return NO_ERROR;
}
将那些文件添加到对应真实的路径中
所以到目前为止我们就大概将这些梳理了一遍。
writeProguardFile
然后我们在分析一下最后layout的输出结果:
status_t
writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
{
status_t err = -1;
if (!bundle->getProguardFile()) {
return NO_ERROR;
}
ProguardKeepSet keep;
err = writeProguardForAndroidManifest(&keep, assets);
if (err < 0) {
return err;
}
err = writeProguardForLayouts(&keep, assets);
if (err < 0) {
return err;
}
FILE* fp = fopen(bundle->getProguardFile(), "w+");
if (fp == NULL) {
fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
bundle->getProguardFile(), strerror(errno));
return UNKNOWN_ERROR;
}
const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules;
const size_t N = rules.size();
for (size_t i=0; i<N; i++) {
const SortedVector<String8>& locations = rules.valueAt(i);
const size_t M = locations.size();
for (size_t j=0; j<M; j++) {
fprintf(fp, "# %s\n", locations.itemAt(j).string());
}
fprintf(fp, "%s\n\n", rules.keyAt(i).string());
}
fclose(fp);
return err;
}
status_t
writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
{
status_t err;
const char* kClass = "class";
const char* kFragment = "fragment";
const String8 kTransition("transition");
const String8 kTransitionPrefix("transition-");
// tag:attribute pairs that should be checked in layout files.
KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs;
addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, kClass);
addTagAttrPair(&kLayoutTagAttrPairs, kFragment, NULL, kClass);
addTagAttrPair(&kLayoutTagAttrPairs, kFragment, RESOURCES_ANDROID_NAMESPACE, "name");
// tag:attribute pairs that should be checked in xml files.
KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs;
addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, kFragment);
addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, kFragment);
// tag:attribute pairs that should be checked in transition files.
KeyedVector<String8, Vector<NamespaceAttributePair> > kTransitionTagAttrPairs;
addTagAttrPair(&kTransitionTagAttrPairs, kTransition.string(), NULL, kClass);
addTagAttrPair(&kTransitionTagAttrPairs, "pathMotion", NULL, kClass);
const Vector<sp<AaptDir> >& dirs = assets->resDirs();
const size_t K = dirs.size();
for (size_t k=0; k<K; k++) {
const sp<AaptDir>& d = dirs.itemAt(k);
const String8& dirName = d->getLeaf();
Vector<String8> startTags;
const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL;
if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
tagAttrPairs = &kLayoutTagAttrPairs;
} else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
startTags.add(String8("PreferenceScreen"));
startTags.add(String8("preference-headers"));
tagAttrPairs = &kXmlTagAttrPairs;
} else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
startTags.add(String8("menu"));
tagAttrPairs = NULL;
} else if (dirName == kTransition || (strncmp(dirName.string(), kTransitionPrefix.string(),
kTransitionPrefix.size()) == 0)) {
tagAttrPairs = &kTransitionTagAttrPairs;
} else {
continue;
}
const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
const size_t N = groups.size();
for (size_t i=0; i<N; i++) {
const sp<AaptGroup>& group = groups.valueAt(i);
const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
const size_t M = files.size();
for (size_t j=0; j<M; j++) {
err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs);
if (err < 0) {
return err;
}
}
}
}
// Handle the overlays
sp<AaptAssets> overlay = assets->getOverlay();
if (overlay.get()) {
return writeProguardForLayouts(keep, overlay);
}
return NO_ERROR;
}
下一次我们着重分析,一个从产生,到安装整个的过程经历了什么