Android资源管理框架-------之资源信息的加载(五)
在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;}
这个比较简单,我们看AssetManager
的addAssetPath
方法的实现:
//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
是否为空。mResources
是AssetManager
类的成员,它的类型为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; }}
也就是说,AssetManager
的addAssetPath
方法,未必会立即去加载资源包中的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
方法总结起来也很简单,就是查缓存、加载资源(这中间可能创建Asset
、ResTable
等对象)、存缓存、异常情况处理(没找到资源时)。但是,由于涉及到等情况比较多,包括: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
中的。当然mResources
是ResTable
的一个实例,也是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);
完成资源包的加载。
到这里AssetManager
的appendPathToResTable
方法总算说完了,这个方法中,最关键的一步是调用ResTable
类的对象mResources
的add
方法。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,创建PackageGroup
、Package
、TypeList
、Type
等对象,并把该chunk中的数据地址写入到这些创建的数据结构中,完成对resources.arsc的解析。后面我们就可以通过这些数据结构,来进行对资源的管理工作了。另外,本文力求简洁,略去了RRO和idmap相关的处理,如果您对这部分感兴趣,请移步Android资源管理中的Runtime Resources Overlay-------之overlay包的加载(四)。
我们这里介绍的东西,绝大部分都和Android资源管理的那些数据结构关系非常紧密,如果您对这些数据结构不太熟悉,可能读起本文来会比较费力,因此建议您先仔细阅读前面的两篇文章,对这些数据结构比较熟悉后,读起本文就会容易许多。
更多相关文章
- Android常见的几种图片加载框架
- android 跳转到横屏activity出现之前activity周期重新加载的问题
- Android开发指南-框架主题-资源和资产
- Android(安卓)GpioService从app到驱动
- Android高性能加载大量图片[前言]
- Android中使用webview加载网页上的按钮点击失效
- Android开发指南-框架主题-资源和资产
- Android(安卓)多语言
- Android使用第三方SDK——百度地图