在Android资源管理框架-------之Android中的资源包(二)一文中,我们讲了Android对资源包的管理,当然这个管理我们说得比较宏观。在了解了Android资源管理相关的一些数据结构后,我们就可以深入地学习Android对资源包的管理和组织了,本篇我们先说资源信息的加载。

        在Android资源管理框架-------之Android中的资源包(二)一文中,我们讲到当应用启动后,framework会通过ResourcesManager.getTopLevelResources()方法来创建AssetManager对象,并调用AssetManager的相关方法,将该应用本身(它自己也是一个资源包)、资源共享库、overlay包统统添加进去。具体怎么实现的呢?我们接着往下看:

    //framework/base/core/java/android/content/res/AssetManager.java        public final int addAssetPath(String path) {        synchronized (this) {            //添加资源包,res是cookie值,也就是该资源包在AssetManager中所有资源包中的索引值+1            int res = addAssetPathNative(path);            //提取出该资源包的Global(Value) String Pool            makeStringBlocks(mStringBlocks);            return res;        }    }    public final int addOverlayPath(String idmapPath) {        synchronized (this) {            //添加资源包,res是cookie值,也就是该资源包在AssetManager中所有资源包中的索引值+1            int res = addOverlayPathNative(idmapPath);            //提取出该资源包的Global(Value) String Pool            makeStringBlocks(mStringBlocks);            return res;        }    }

        其中,添加应用本身和资源共享库调用的是addAssetPath方法,添加overlay包调用的是addOverlayPath方法。makeStringBlocks以及addOverlayPath方法的实现我们分别在Android资源管理框架-------之Android中的资源包(二)和Android资源管理中的Runtime Resources Overlay-------之overlay包的加载(四)中已经讲过,这里不再赘述。我们看addAssetPathNative方法,它对应的native实现是:

//frameworks/base/core/jni/android_util_AssetManager.cppstatic jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,                                                       jstring path){    //判空检查    ScopedUtfChars path8(env, path);    if (path8.c_str() == NULL) {        return 0;    }    //拿到native层的AssetManager对象的地址,其实这个地址保存在java层的一个变量中,这里只是获取一下    AssetManager* am = assetManagerForJavaObject(env, clazz);    if (am == NULL) {        return 0;    }    int32_t cookie;    //添加之,cookie是输出参数    bool res = am->addAssetPath(String8(path8.c_str()), &cookie);    //添加成功则返回cookie值,否则返回0    return (res) ? static_cast<jint>(cookie) : 0;}

        这个比较简单,我们看AssetManageraddAssetPath方法的实现:

//frameworks/base/libs/androidfw/AssetManager.cppbool AssetManager::addAssetPath(const String8& path, int32_t* cookie){    //路径合法性检查,略去        ......        //如果已经添加过则不再重复添加    for (size_t i=0; i<mAssetPaths.size(); i++) {        if (mAssetPaths[i].path == ap.path) {            if (cookie) {                //cookie 的值为 索引 + 1                *cookie = static_cast<int32_t>(i+1);            }            return true;        }    }    //检查资源包中是否存在AndroidManifest.xml,如果不存在,则返回false    ......    //路径列表中添加该路径    mAssetPaths.add(ap);    if (cookie) {        //cookie 的值为 索引 + 1        *cookie = static_cast<int32_t>(mAssetPaths.size());    }   #ifdef HAVE_ANDROID_OS    // 顺便还会查找和添加overlay package    asset_path oap;    for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {        mAssetPaths.add(oap);    }   #endif    //这里才是重点,资源包中的resources.arsc就是在这里加载的    if (mResources != NULL) {        appendPathToResTable(ap);    }    return true;}

        我们看到,这个方法主要做了两件事情:把path加入到mAssetPaths中去;加载path对应的资源包中的resources.arsc。其中,后者是在appendPathToResTable方法中实现的。我们注意到,执行这个方法前先去判断mResources是否为空。mResourcesAssetManager类的成员,它的类型为ResTable,并且在构造AssetManager对象的时候,并不会给这个成员赋值,也就是说,如果在AssetManager对象刚构造完,就调用addAssetPath方法添加资源包,这个时候,资源包是不会被加载的。那这种情况下,到底在什么时候加载呢?

//frameworks/base/libs/androidfw/AssetManager.cppconst ResTable* AssetManager::getResTable(bool required) const{    //如果已经构造过,则返回    ResTable* rt = mResources;    if (rt) {        return rt;    }    mResources = new ResTable();    //主要是更新Locale信息,设置资源配置信息    updateResourceParamsLocked();    bool onlyEmptyResources = true;    const size_t N = mAssetPaths.size();    //在这里会按mAssetPaths中所有的路径来加载resources.arsc    for (size_t i=0; i<N; i++) {        bool empty = appendPathToResTable(mAssetPaths.itemAt(i));        onlyEmptyResources = onlyEmptyResources && empty;    }    //required默认是true,还未见用false的场景,也就是说只要mAssetPaths中    //有一个路径下找不到resources.arsc,就会清空mResources    if (required && onlyEmptyResources) {        ALOGW("Unable to find resources file resources.arsc");        delete mResources;        mResources = NULL;    }}

        也就是说,AssetManageraddAssetPath方法,未必会立即去加载资源包中的resources.arsc,有可能会有一个延迟,延迟到构造它的mResources成员,也就是ResTable实例的时候再去加载。那么这个getResTable方法具体会在什么时候调用呢?在我们查寻AssetManager中资源id、资源项、加载资源、获取StringBlock等的时候,都会调用这个方法,进而加载resources.arsc。

        接下来看看appendPathToResTable是如何加载resources.arsc的:

//frameworks/base/libs/androidfw/AssetManager.cppbool AssetManager::appendPathToResTable(const asset_path& ap) const {    /*     * 在这里ass表示我们的资源包中的resources.arsc,     * sharedRes则是用来存放资源包中的resources.arsc中的具体数据     * 由于一个ResTable对象可以添加多个资源包,所以ResTable可以简单认为是Asset的集合     */    Asset* ass = NULL;    ResTable* sharedRes = NULL;        bool shared = true;    bool onlyEmptyResources = true;    MY_TRACE_BEGIN(ap.path.string());    //idmap 是RRO相关的概念,这里不再多说    Asset* idmap = openIdmapLocked(ap);    /*     * 这里要先看一下已经加载过多少个资源包了,     * 如果没有加载过,那么就认为默认加载的头一个资源包是framework-res.apk     * 也就是Android的系统资源包     */    size_t nextEntryIdx = mResources->getTableCount();    /*     * 我们要加载resources.arsc,大多数情况这个文件位于压缩包也就是apk中     * 对应我们这个if分支     * 但AssetManager也是支持对解压出来的resources.arsc的     * 这种情况对应下面的else分支     */     //resources.arsc在apk中    if (ap.type != kFileTypeDirectory) {                if (nextEntryIdx == 0) {//加载的是framework-res.apk,也就是Google的Android系统资源包            //先去缓存中查找系统资源包及其overlay包是否已经加载过,如果加载过,同时会创建            //ResTable对象,并缓存,也就是sharedRes得到的值肯定不为空            sharedRes = const_cast<AssetManager*>(this)->                mZipSet.getZipResourceTable(ap.path);            if (sharedRes != NULL) {                /**                 * 如果已经创建过了,那么要添加的这个资源包,就应该放到它们后面                 * 这种情况出现在在同一个进程创建多个不同的AssetManager或者Resources对象的时候,                 * 每个AssetManager中都会添加系统资源包,这时候就用上这个缓存了                 */                 nextEntryIdx = sharedRes->getTableCount();            }        }        //如果当前要加载的不是系统资源或者是系统资源,但是之前并未加载过,走这里        if (sharedRes == NULL) {            //查缓存,看看之前是否加载并创建过Asset对象            ass = const_cast<AssetManager*>(this)->                mZipSet.getZipResourceTableAsset(ap.path);            //如果没有找到,则加载之,即打开resources.arsc,并将得到的ass放入缓存            if (ass == NULL) {                ALOGV("loading resource table %s\n", ap.path.string());                //打开resources.arsc                ass = const_cast<AssetManager*>(this)->                    openNonAssetInPathLocked("resources.arsc",                                             Asset::ACCESS_BUFFER,                                             ap);                //放入缓存                if (ass != NULL && ass != kExcludedAsset) {                    ass = const_cast<AssetManager*>(this)->                        mZipSet.setZipResourceTableAsset(ap.path, ass);                }            }            //如果要加载的是系统资源,但这时候还从未加载过,则连同系统资源包的overlay package一并加载            if (nextEntryIdx == 0 && ass != NULL) {                // If this is the first resource table in the asset                // manager, then we are going to cache it so that we                // can quickly copy it out for others.                ALOGV("Creating shared resources for %s", ap.path.string());                //创建ResTable对象                sharedRes = new ResTable();                //加入资源包中的resources.arsc以及idmap                sharedRes->add(ass, idmap, nextEntryIdx + 1, false);#ifdef HAVE_ANDROID_OS                                const char* data = getenv("ANDROID_DATA");                LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");                String8 overlaysListPath(data);                overlaysListPath.appendPath(kResourceCache);                overlaysListPath.appendPath("overlays.list");                //加载系统资源包的overlay package,并将这些资源包也加入sharedRes,                addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);#endif                //缓存sharedRes                sharedRes = const_cast<AssetManager*>(this)->                    mZipSet.setZipResourceTable(ap.path, sharedRes);            }        }    } else {//resources.arsc不在apk中,而在某个路径下        //加载到内存,创建Asset对象        ass = const_cast<AssetManager*>(this)->            openNonAssetInPathLocked("resources.arsc",                                     Asset::ACCESS_BUFFER,                                     ap);        //不是apk中的不缓存,不共享,添完就删        shared = false;    }    if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {        if (sharedRes != NULL) {            ALOGV("Copying existing resources for %s", ap.path.string());            //如果是系统资源包,则将系统资源包连同它的overlay package一起添加到我们的            //mResources对象当中去            mResources->add(sharedRes);        } else {            ALOGV("Parsing resources for %s", ap.path.string());            //否则只添加我们要加载的这个资源包(其实仅仅是它的resources.arsc和idmap文件而已)            mResources->add(ass, idmap, nextEntryIdx + 1, !shared);        }        onlyEmptyResources = false;        //不共享的话,就没必要留着了        if (!shared) {            delete ass;        }    } else {        //没找到资源        ALOGV("Installing empty resources in to table %p\n", mResources);        mResources->addEmpty(nextEntryIdx + 1);    }    if (idmap != NULL) {        delete idmap;    }    MY_TRACE_END();    return onlyEmptyResources;}

        appendPathToResTable方法总结起来也很简单,就是查缓存、加载资源(这中间可能创建AssetResTable等对象)、存缓存、异常情况处理(没找到资源时)。但是,由于涉及到等情况比较多,包括:Zygote起来后preload系统资源、应用加载系统资源、应用第一次加载其它资源(包括应用本身、资源共享库、overlay package)、应用第二次加载其它资源共四种情况。

        先说Zygote起来后preload的这种情况。我们知道,在Zygote进程加载完VM,进入java世界后,会去执行ZygoteInit.java的main方法。它会创建serverSocket,用来响应SystemServier进程发出的创建Android应用进程的请求,然后就会预加载很多东西了,包括我们常用的类以及Android的系统资源。这样在Android应用进程从Zygote进程fork出来以后,就不用重新加载了,节约应用的启动时间。这个时候它会创键一个system AssetManager,此时appendPathToResTable方法的流程是:

                nextEntryIdx = 0,从mZipSet缓存中获取sharedRes,结果是NULL,然后从mZipSet缓存中获取ass,结果还是NULL,然后调用openNonAssetInPathLocked打开系统资源包中的resources.arsc文件,并赋值给ass,并把该ass存入mZipSet缓存中,以后就可以从缓存中获取了。然后创建ResTable对象并赋给sharedRes,并把ass添加进sharedRes,然后同样加载系统资源包的overlay包,并添加到sharedRes中。然后再把sharedRes整个存入缓存,这样以后加载系统资源包,就不用一个一个ass地添加了,而是把整个sharedRes一起添加进去就可以了,然后我们看到mResources->add(sharedRes);这一句就是连同系统资源包和它的overlay包一并添加到mResources中的。当然mResourcesResTable的一个实例,也是Assetmanager中最重要的一个成员,资源都加载到它里面了。这样这个system的AssetManager的资源就加载完了。当然,overlay package是如何被添加到sharedRes中的,我们一句带过,想要详细了解请参考Android资源管理中的Runtime Resources Overlay-------之overlay包的加载(四)。

        再说应用加载系统资源的这种情况,我们前文讲过每一个Android应用的AssetManager中都会加载系统的资源信息,具体是怎么加载的详见Android资源管理框架-------之Android中的资源包(二)。在加载的时候appendPathToResTable方法的流程是:

                nextEntryIdx = 0,从mZipSet缓存中获取sharedRes,结果不为NULL,修正nextEntryIdx = sharedRes->getTableCount();mResources->add(sharedRes);这样,系统资源包就都添加到mResources中了。

        我们再看应用第一次加载其它资源(包括应用本身、资源共享库、overlay package)的情况:nextEntryIdx = mResources->getTableCount();不为0,因为此时已经添加过系统资源包及其overlay包了,sharedRes为空,缓存中查找ass为空,调用openNonAssetInPathLocked方法打开资源包中的resources.arsc文件,并赋值给ass,并把该ass存入mZipSet缓存中,以后就可以从缓存中获取了。此时ass不为空,调用mResources->add(ass, idmap, nextEntryIdx + 1, !shared);完成资源包的加载。

        最后看应用第二次加载其它资源(包括应用本身、资源共享库、overlay package)的情况:nextEntryIdx = mResources->getTableCount();不为0,因为此时已经添加过系统资源包及其overlay包了,sharedRes为空,缓存中查找ass不为空,调用mResources->add(ass, idmap, nextEntryIdx + 1, !shared);完成资源包的加载。

        到这里AssetManagerappendPathToResTable方法总算说完了,这个方法中,最关键的一步是调用ResTable类的对象mResourcesadd方法。add方法重载了好多个,但最终都会走到一下两个方法中的某一个:

//frameworks/base/libs/androidfw/ResourceTypes.cppstatus_t ResTable::add(ResTable* src){    mError = src->mError;    //添加Headers    for (size_t i=0; i<src->mHeaders.size(); i++) {        mHeaders.add(src->mHeaders[i]);    }    //添加PackageGroups    for (size_t i=0; i<src->mPackageGroups.size(); i++) {        PackageGroup* srcPg = src->mPackageGroups[i];        PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);        //对每个PackageGroup,添加package        //需要说明的是overlay package 会和它的target package 放到同一个PackageGroup中        for (size_t j=0; j<srcPg->packages.size(); j++) {            pg->packages.add(srcPg->packages[j]);        }        ///对每个PackageGroup添加TypeList        for (size_t j = 0; j < srcPg->types.size(); j++) {            if (srcPg->types[j].isEmpty()) {                continue;            }            TypeList& typeList = pg->types.editItemAt(j);            typeList.appendVector(srcPg->types[j]);        }        //添加DynamicRefTable        pg->dynamicRefTable.addMappings(srcPg->dynamicRefTable);        pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId);        mPackageGroups.add(pg);    }    //    memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));    return mError;}

        这个方法非常简单,就是对每个ResTable中的数据结构进行添加。DynamicRefTable是资源共享库相关的东西,详见Android资源管理中的SharedLibrary和Dynamic Reference-------之AssetManager的处理(四)。下面我们看另外一个方法:

//frameworks/base/libs/androidfw/ResourceTypes.cpp/* * data 从表示resources.arsc的Asset对象中获取,表示resources.arsc文件的内容 * dataSize data的大小 * idmapData 从表示idmap文件的Asset对象中获取,表示idmap文件的内容 * idmapDataSize idmapData的大小 * cookie 该资源包的index + 1 * copyData是否复制data到该ResTable中,对于appendPathToResTable方法中shared * 为false的copyData为true,否则为false */status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData,             size_t idmapDataSize, const int32_t cookie, bool copyData){    //......例行检查,代码省略     //一个Header对应一个resources.arsc,也就是一个资源包    Header* header = new Header(this);    //mHeaders中的元素一般和资源包也就是resources.arsc是对应的    header->index = mHeaders.size();    //cookie = mHeaders.size() + 1    header->cookie = cookie;    //为idmap数据分配内存,header也会持有其指针    if (idmapData != NULL) {        header->resourceIDMap = (uint32_t*) malloc(idmapDataSize);        if (header->resourceIDMap == NULL) {            delete header;            return (mError = NO_MEMORY);        }        memcpy(header->resourceIDMap, idmapData, idmapDataSize);        header->resourceIDMapSize = idmapDataSize;    }    //mHeaders表示这个ResTable(或者说AssetManager中所有的资源包信息)    mHeaders.add(header);        //......大小端的处理,如有必要还会复制resources.arsc的数据    //resources.arsc开头就是整个文件数据的header    header->header = (const ResTable_header*)data;    header->size = dtohl(header->header->header.size);    //dataEnd,起始地址加上整个resources.arsc的大小    header->dataEnd = ((const uint8_t*)header->header) + header->size;    size_t curPackage = 0;    /**    *跳过resources.arsc中第一个header    *也就是整个资源索引表的header    *需要说明的是整个resources.arsc有一个总的header,前面我们    *已经解析过,所以跳过    *另外,resources.arsc的一级子元素只有两种类型:    *RES_STRING_POOL_TYPE类型的global string pool    *RES_TABLE_PACKAGE_TYPE类型的资源包    */    const ResChunk_header* chunk =        (const ResChunk_header*)(((const uint8_t*)header->header)                                 + dtohs(header->header->header.headerSize));    //遍历解析每一个子元素    while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) &&           ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) {        //获取当前子元素的数据块的大小和类型        const size_t csize = dtohl(chunk->size);        const uint16_t ctype = dtohs(chunk->type);        if (ctype == RES_STRING_POOL_TYPE) {            // global string pool            if (header->values.getError() != NO_ERROR) {                //把global string pool提取出来                status_t err = header->values.setTo(chunk, csize);                if (err != NO_ERROR) {                    return (mError=err);                }            } else {                ALOGW("Multiple string chunks found in resource table.");            }        } else if (ctype == RES_TABLE_PACKAGE_TYPE) {            //解析包信息            if (curPackage >= dtohl(header->header->packageCount)) {                ALOGW("More package chunks were found than the %d declared in the header.",                     dtohl(header->header->packageCount));                return (mError=BAD_TYPE);            }            //具体解析工作交给parsePackage方法            if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) {                return mError;            }            curPackage++;       } else {           ALOGW("Unknown chunk type 0x%x in table at %p.\n",                ctype,                    (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));       }       //处理下一个子元素       chunk = (const ResChunk_header*)            (((const uint8_t*)chunk) + csize);    }    //一系列检查,先不必关注    if (curPackage < dtohl(header->header->packageCount)) {        //没找全        ALOGW("Fewer package chunks (%d) were found than the %d declared in the header.",             (int)curPackage, dtohl(header->header->packageCount));        return (mError=BAD_TYPE);    }    mError = header->values.getError();    if (mError != NO_ERROR) {        //包中没有global string pool        ALOGW("No string values found in resource table!");    }    TABLE_NOISY(ALOGV("Returning from add with mError=%d\n", mError));    return mError;}

        这个方法其实就是解析resources.arsc的二级chunk,然后通过ResTable::Header的形式记录之,并把它添加到ResTable的mHeaders中。我们接下来看parsePackage方法,这个方法比较长,跟本文关系不大的内容这里就不贴出来了:

//frameworks/base/libs/androidfw/ResourceTypes.cppstatus_t ResTable::parsePackage(const ResTable_package* const pkg,                                const Header* const header){     const uint8_t* base = (const uint8_t*)pkg;     const uint32_t pkgSize = dtohl(pkg->header.size);     //拿到这个资源包resources.arsc中写的包id     uint32_t id = dtohl(pkg->id);          //idmap相关的东西,和本文关系不大,略去......          PackageGroup* group = NULL;     Package* package = new Package(this, header, pkg);     /**     *写入type string pool,不过最后一个参数是错的     *不过ResStringPool有自己的解析方法,这个参数的值无所谓     */     err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),                                   header->dataEnd-(base+dtohl(pkg->typeStrings)));     /**     *写入type string pool,不过最后一个参数是错的     *不过ResStringPool有自己的解析方法,这个参数的值无所谓     */     err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),                                  header->dataEnd-(base+dtohl(pkg->keyStrings)));     //mPackageMap的值-1就是该id对应的包所在的PackageGroup在mPackageGroups中的索引     //另外我们看到,这个id的值是target Package的id也就是说此时它是按target pacakge来处理的     size_t idx = mPackageMap[id];     if (idx == 0) {          //idx = 0,表示这个包还没有加载过,修正为它真正的idx也就是index + 1         idx = mPackageGroups.size() + 1;         //获取包名         char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];         strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));         /**          * 创建PackageGroup对象,我们不考虑overlay package的情况下,          * 一个资源包,也就对应一个PackageGroup          */         group = new PackageGroup(this, String16(tmpName), id);         if (group == NULL) {            delete package;            return (mError=NO_MEMORY);        }        //添加进去        err = mPackageGroups.add(group);        if (err < NO_ERROR) {            return (mError=err);        }        //资源共享库相关的处理,这里可以不用关注        size_t N = mPackageGroups.size();        for (size_t i = 0; i < N; i++) {            //给ResTable中的所有PackageGroup,添加运行时该包的id,            //方便其它包引用当前包里面的资源            mPackageGroups[i]->dynamicRefTable.addMapping(                    group->name, static_cast<uint8_t>(group->id));        }     } else {         //这种情况下,如果有overlay package,我们在这里不考虑,略去......         //否则就是往同一个AssetManager对象中,添加了两个id相同的包,         //最好不要这么干,有可能引起typeId混乱,而出错。     }     //把new出来的package添加到group中     err = group->packages.add(package);     //跳过package的header     const ResChunk_header* chunk =        (const ResChunk_header*)(((const uint8_t*)pkg)                                 + dtohs(pkg->header.headerSize));     const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size);     /**     *解析resources.arsc中package数据块的内部数据     *一共有三种子结构(type stringpool、keystring pool除外,前面我们已经提取这两个了):     *RES_TABLE_TYPE_SPEC_TYPE     *RES_TABLE_TYPE_TYPE     *RES_TABLE_LIBRARY_TYPE     */     while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) &&           ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) {         //当前数据块的大小和类型         const size_t csize = dtohl(chunk->size);         const uint16_t ctype = dtohs(chunk->type);         //处理TYPE_SPEC数据块         if (ctype == RES_TABLE_TYPE_SPEC_TYPE) {             const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk);             //拿到占用的空间和entryCount             const size_t typeSpecSize = dtohl(typeSpec->header.size);             const size_t newEntryCount = dtohl(typeSpec->entryCount);             //有entry的话             if (newEntryCount > 0) {                 //type都是从1开始计数的,所以要 - 1                 uint8_t typeIndex = typeSpec->id - 1;                                  //idmap相关的东西,略去......                 //根据typeIndex直接找到typeList,不考虑overlay package的情况下,此时的typeList应该是空的                 TypeList& typeList = group->types.editItemAt(typeIndex);                 if (!typeList.isEmpty()) {                     //target包的Type                     const Type* existingType = typeList[0];                     /**                     *这个if语句出现的情景是:同一个AssetManager加载了两个id相同的包                     */                     if (existingType->entryCount != newEntryCount && idmapIndex < 0) {                         ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",                             (int) newEntryCount, (int) existingType->entryCount);                         // We should normally abort here, but some legacy apps declare                         // resources in the 'android' package (old bug in AAPT).                     }                 }                 //创建type并写入相关信息                 Type* t = new Type(header, package, newEntryCount);                 t->typeSpec = typeSpec;                 t->typeSpecFlags = (const uint32_t*)(                    ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));                                  //idmap相关的东西,略去......                                  //加入到target pakage对应的typeList里面                 typeList.add(t);             }         } else if (ctype == RES_TABLE_TYPE_TYPE) {             const ResTable_type* type = (const ResTable_type*)(chunk);             //拿到占用的空间和entryCount             const uint32_t typeSize = dtohl(type->header.size);             const size_t newEntryCount = dtohl(type->entryCount);             //如果有entry             if (newEntryCount > 0) {                //该type的索引                uint8_t typeIndex = type->id - 1;                                                  //idmap相关的东西,略去......                                 //拿到我们之前添加进去的Type对象                Type* t = typeList.editItemAt(typeList.size() - 1);                //把该种配置下的资源添加进去                t->configs.add(type);             }         } else if (ctype == RES_TABLE_LIBRARY_TYPE) {             //资源共享库相关的东西,和本文关系不大,如有兴趣:             //https://blog.csdn.net/dayong198866/article/details/95226237         } else {                 //......         }         chunk = (const ResChunk_header*)            (((const uint8_t*)chunk) + csize);     }     return NO_ERROR;}

        这个方法就是根据传过来的resources.arsc中的RES_TABLE_PACKAGE_TYPE类型的chunk,创建PackageGroupPackageTypeListType等对象,并把该chunk中的数据地址写入到这些创建的数据结构中,完成对resources.arsc的解析。后面我们就可以通过这些数据结构,来进行对资源的管理工作了。另外,本文力求简洁,略去了RRO和idmap相关的处理,如果您对这部分感兴趣,请移步Android资源管理中的Runtime Resources Overlay-------之overlay包的加载(四)。

        我们这里介绍的东西,绝大部分都和Android资源管理的那些数据结构关系非常紧密,如果您对这些数据结构不太熟悉,可能读起本文来会比较费力,因此建议您先仔细阅读前面的两篇文章,对这些数据结构比较熟悉后,读起本文就会容易许多。

更多相关文章

  1. Android常见的几种图片加载框架
  2. android 跳转到横屏activity出现之前activity周期重新加载的问题
  3. Android开发指南-框架主题-资源和资产
  4. Android(安卓)GpioService从app到驱动
  5. Android高性能加载大量图片[前言]
  6. Android中使用webview加载网页上的按钮点击失效
  7. Android开发指南-框架主题-资源和资产
  8. Android(安卓)多语言
  9. Android使用第三方SDK——百度地图

随机推荐

  1. Android中的消息处理实例与分析
  2. Android:什么情况?
  3. Android深入理解android:layout_gravity
  4. android布局文件中各属性所代表的意义
  5. Android(安卓)WebView与 JS 的交互方式
  6. android:layout_gravity和android:gravit
  7. android:layout_gravity和android:gravit
  8. android:layout_gravity和android:gravit
  9. android中线程进程模型
  10. android:layout_gravity和android:gravit