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.png

assets->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路径如何添加呢?

这块逻辑就很简单了,就是获取路径然后将路径保存在AaptAssets中主要分成三类:

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对应的资源的类型,分别是:

第二个核心就是对应生成那些类似于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;
}

这个过程也比较简单,就不画图了

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;
}

下一次我们着重分析,一个从产生,到安装整个的过程经历了什么

上一篇下一篇

猜你喜欢

热点阅读