美文网首页
APK资源文件的生成

APK资源文件的生成

作者: 我叫王菜鸟 | 来源:发表于2017-11-27 17:39 被阅读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路径如何添加呢?

    • 将路径添加到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;
    }
    
    

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

    相关文章

      网友评论

          本文标题:APK资源文件的生成

          本文链接:https://www.haomeiwen.com/subject/vqfzvxtx.html