大家好,关于Android 6.0时代:深入解析应用资源管理对象创建策略很多朋友都还不太明白,今天小编就来为大家分享关于的知识,希望对各位有所帮助!
其中Resources类可以根据ID查找资源,AssetManager类可以根据文件名查找资源。
事实上,从资源搜索过程来看,可以分为两大类。第一类资源没有对应的文件,第二类资源有文件。例如,字符串资源直接编译在resources.arsc文件中,而界面布局资源则对应在APK包中。的单独文件。
如果一个资源ID对应一个文件,那么Resources类首先根据该ID找到资源文件名,然后将文件名传递给AssetManager类来打开对应的文件。基本流程如下:
resources-3.jpg 而且这两个类都有相应的缓存机制来缓存资源,这样下次使用的时候就不需要再去查找和加载了。
App的Resources对象创建过程
既然这两个类负责管理Android资源,那么我们需要弄清楚这两个类的对象是如何以及何时在应用程序中创建的。
资源将包含一个AssetManager 对象。首先,重点关注Resources对象的创建过程。首先看一个整体时序图:
resources-4.jpg 创建的资源对象将缓存在LoadedApk中。并且在创建context时,LoadedApk是相同的,因此同一个应用中的不同ContextImpl获取的是同一组资源。
ActivityThread类的成员变量mActiveResources指向一个HashMap。这个HashMap用来维护当前应用进程中加载的每个Apk文件与其对应的Resources对象的对应关系。
也就是说,给定一个Apk文件路径,ActivityThread类的成员函数getTopLevelResources就可以检查成员变量mActiveResources中是否存在对应的Resources对象。如果不存在,就会创建一个新的,并保存在ActivityThread类的成员变量mActiveResources中。
Context中提供了getResources()方法来获取资源对象,因此在Activity中可以方便地获取该对象:
资源res=getResources(); Activity中的context实际上是对ComtextImpl的封装,所以最终是通过ContextImpl.getResources()来获取resources对象的:
公共资源getResources() {
返回mResources;
}而mResources是ContextImpl的属性成员。而mResources是在ContextImpl的构造函数中初始化的。
看一下ContextImpl的构造函数:
私有ContextImpl(ContextImpl容器,ActivityThread主线程,
LoadedApk packageInfo、IBinder ActivityToken、UserHandle 用户、布尔限制、
显示显示,配置overrideConfiguration,int createDisplayWithId) {
…………
资源resources=packageInfo.getResources(mainThread);
如果(资源!=null){
//由于6.0不支持多屏显示,因此不会走该分支。虽然相关代码已经很多了,但多屏操作在7.0才正式支持。
if (displayId !=Display.DEFAULT_DISPLAY
||覆盖配置!=null
|| (compatInfo !=null compatInfo.applicationScale
!=resources.getCompatibilityInfo().applicationScale)) {
.
}
}
mResources=资源;
.从ContextImpl构造函数中发现,是通过传入的LoadedApk对象的getResources()方法获取Resources对象:
公共资源getResources(ActivityThread mainThread) {
//缓存机制,如果LoadedApk中的mResources已经初始化,则直接返回。
//否则,通过ActivityThread创建资源对象
if (mResources==null) {
mResources=mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,
mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
}
返回mResources;
在LoadedApk.getResources()方法中,首先判断mResources是否为null。如果为null,则调用ActivityThread.getTopLevelResources()方法获取Resources对象。
这里需要注意的是,LoadedApk类有两种构造方法:一种用于系统app,一种用于普通app。普通应用的构造方法中没有初始化mResources的值。系统应用LoadedApk使用的构造方法中,mResources的值被初始化为Resources.getSystem()。
这里我们只关心普通应用程序的LoadedApk对象是在没有初始化mResources对象的情况下创建的。那么当第一次调用LoadedApk对象的getResources()方法时,会通过调用以下方法创建一个Resources对象并缓存起来。以后通过LoadedApk.getResources()获取时,不需要重新创建Resources对象。只需返回到您之前创建的内容即可。
/**
* 为给定包创建顶级资源。
*/
资源getTopLevelResources(
String resDir, //app资源文件夹路径,其实就是apk文件的路径,如/data/app/包名/base.apk
String[] splitResDirs, //当一个app由多个apk组成时(将原apk切分成若干个apk),每个子apk中的资源文件夹
字符串[]覆盖目录,
String[] libDirs, //app依赖的共享jar/apk路径
int 显示ID,
配置覆盖配置,
LoadedApk pkgInfo //代表正在运行的应用程序
){
返回mResourcesManager.getTopLevelResources(resDir,splitResDirs,overlayDirs,libDirs,
displayId、overrideConfiguration、pkgInfo.getCompatibilityInfo());
}ActivityThread.getTopLevelResources()方法通过ResourcesManager类的getTopLevelResources()方法创建Resources对象:
/**
* 为具有给定兼容性信息的应用程序创建顶级资源。
*
* @param resDir 资源目录。
* @param splitResDirs 分割资源目录。
* @param overrideDirs 资源覆盖目录。
* @param libDirs 此应用程序引用的共享库资源目录。
* @param displayId 显示Id。
* @param overrideConfiguration 覆盖配置。
* @param compatInfo 兼容性信息。不得为空。
*/
资源getTopLevelResources(String resDir, String[] splitResDirs,
String[] OverlayDirs, String[] libDirs, int displayId,
配置overrideConfiguration, CompatibilityInfo compatInfo) {
……………………
//以apk路径作为参数创建密钥
ResourcesKey key=new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
资源;
同步(这个){
//资源取决于应用程序规模。
if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + "/" + scale);
//检查apk对应的resources对象是否已经存在
WeakReferencewr=mActiveResources.get(key);
r=wr !=null ? 空;
//if (r !=null) Log.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
if (r !=null r.getAssets().isUpToDate()) {
if (DEBUG) Slog.w(TAG, "返回缓存资源" + r + " " + resDir
+ ": appScale=" + r.getCompatibilityInfo().applicationScale
+ " key=" + key + " overrideConfig=" + overrideConfiguration);
返回r;
}
}
//创建AssetManager对象
AssetManager 资产=new AssetManager();
//将应用程序中的资源路径添加到AssetManager对象中
if (resDir !=null) {
if (assets.addAssetPath(resDir)==0) {
返回空值;
}
}
如果(splitResDirs!=null){
for (字符串splitResDir : splitResDirs) {
if (assets.addAssetPath(splitResDir)==0) {
返回空值;
}
}
}
如果(overlayDirs!=null){
for (String idmapPath : overrideDirs) {
资产.addOverlayPath(idmapPath);
}
}
if (libDirs !=null) {
for (String libDir : libDirs) {
//只选择共享依赖中的apk,因为jar中不会有资源文件
if (libDir.endsWith(".apk")) {
if (assets.addAssetPath(libDir)==0) {
Log.w(TAG, "资源路径"" + libDir +
""不存在或不包含任何资源。");
}
}
}
}
…………
r=新资源(资产、dm、配置、compatInfo);
...
mActiveResources.put(key, new WeakReference(r));
返回r
}创建Resources对象时,有一个创建AssetManager对象并向其添加应用程序资源路径的过程。
现在是时候回答Resources 对象何时创建的问题了:
当普通应用对应的LoadedApk对象第一次调用LoadedApk.getResources()方法时,由于LoadedApk中没有缓存,因此会创建该对象并缓存。当稍后再次调用LoadedApk.getResources()方法时,Resources对象将不会被创建,因为它被缓存了。
当创建ContextImpl 对象时,不一定会创建新的Resources 对象。通常,正在运行的应用程序的资源对象仅创建一次。并缓存在LoadedApk对象中。一个正在运行的应用程序可以有多个Context,但每个Context 都包含相同的LoadedApk 对象。
管理系统资源的Resources对象
Resources类中的关键数据成员是:
//在zygote中缓存预加载的资源
私有静态最终LongSparseArray[] sPreloadedDrawables;
私有静态最终LongSparseArraysPreloadedColorDrawables
=新的LongSparseArray();
私有静态最终LongSparseArraysPreloadedColorStateLists=new LongSparseArray();
//引用系统资源实例
静态资源mSystem=null;
//drawable和ColorStateList的缓存
私有最终DrawableCache mDrawableCache=new DrawableCache(this);
私有最终DrawableCache mColorDrawableCache=new DrawableCache(this);
私有最终ConfigurationBoundResourceCachemColorStateListCache=
新的ConfigurationBoundResourceCache(this);
私有最终ConfigurationBoundResourceCachemAnimatorCache=
新的ConfigurationBoundResourceCache(this);
私有最终ConfigurationBoundResourceCachemStateListAnimatorCache=
新的ConfigurationBoundResourceCache(this);
//xml文件的缓存
私有int mLastCachedXmlBlockIndex=-1;
私有最终int[] mCachedXmlBlockIds={ 0, 0, 0, 0 };
私有最终XmlBlock[] mCachedXmlBlocks=new XmlBlock[4];
//AssetManager实例的引用非常关键。
最终的AssetManager mAssets;对于上述静态类型的关键成员,在内存中只有一份,因此必须知道这些静态成员是在何时何地初始化的。
以mSystem为例,如下图:
ZygoteInit的preloadResources()方法中初始化的mSystem值意味着mSystem是由Zygote进程初始化的,所以它孵化的App进程肯定会继承这个东西。
mSystem主要负责预加载Android系统本身提供的资源(framework-res.apk)。加载后的资源存储在其他静态成员变量中,App以后可以通过它们访问系统资源。
zygote进程启动时只加载framework-res.apk的部分系统资源,而不是全部资源。将加载的资源在Framework中的res/values/arrays.xml中定义,例如Leaner等布局资源。
对于那些没有“预加载”的系统资源,它们不被视为缓冲到静态列表变量中。在这种情况下,如果多个应用程序进程需要非预加载的资源,它们将在各自的进程中维护。资源缓冲区。
事实上,App在创建Resources对象时,会在其中添加framework-res.apk,这样就可以访问未预加载的资源。
AssetManager对象的创建过程
从前面的分析可以看出,Android系统中实际管理资源的是AssetManager类。每个Resources 对象都与一个AssetManager 对象关联,Resources 将资源上的大部分操作委托给AssetManager。
另外,还会有一个native层的AssetManager对象与java层的AssetManager对象对应,而native层的AssetManager对象的内存地址存储在java层的AssetManager.mObject中。因此,在Java层AssetManager的jni方法中,可以快速找到其对应的native层的AssetManager对象。
普通应用创建AssetManager对象的过程如下:
创建AssetManager对象时,默认会通过addAssetPath()方法将system/framework/framework-res.apk添加到native层的AssetManager对象中。
AssetManager 类有两个构造函数:
一种是App创建Resources对象时使用的公共类型构造函数:
公共AssetManager() {
同步(这个){
如果(DEBUG_REFS){
mNumRefs=0;
incRefsLocked(this.hashCode());
}
初始化(假);
if (localLOGV) Log.v(TAG, "新资产管理器: " + this);
确保系统资产();
}
}一个是创建管理预加载系统资源的资源对象时使用的私有类型构造函数:
私有AssetManager(布尔isSystem){
如果(DEBUG_REFS){
同步(这个){
mNumRefs=0;
incRefsLocked(this.hashCode());
}
}
初始化(真);
if (localLOGV) Log.v(TAG, "新资产管理器: " + this);
}构造方法中会调用init()方法,该方法是native方法:
源代码路径:
框架/base/core/jni/android_util_AssetManager.cpp
Frameworks/base/libs/androidfw/AssetManager.cppstatic void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
如果(是系统){
验证系统Idmaps();
}
AssetManager* am=new AssetManager();
如果(am==NUL
L) { jniThrowException(env, "java/lang/OutOfMemoryError", ""); return; } am->addDefaultAssets(); ALOGV("Created AssetManager %p for Java object %pn", am, clazz); env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); }在init()方法中创建一个native层中的AssetManager对象. 其中addDefaultAssets()方法将system/framework/framework-res.apk通过addAssetPath()方法加入到native层的AssetManager对象中. bool AssetManager::addDefaultAssets() { const char* root = getenv("ANDROID_ROOT"); LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set"); String8 path(root); // framework/framework-res.apk path.appendPath(kSystemAssets); return addAssetPath(path, NULL); }addAssetPath()方法其实很简单主要是将要加入的资源路径加入到 AssetManager类的成员mAssetPaths中,当mAssetPaths中包含这个资源路径时,不会再次加入。也就是说同一种资源是不会被重复加载的。 通过以上代码可知在创建一个Java层的AssetManager对象时,会创建一个native层的AssetManager对象,并把system/framework/framework-res.apk加入到资源路径集合mAssetPaths中去。 而且在前面介绍getTopLevelResources()时,也可以看到,当创建AssetManager对象之后,还会把app的资源路径,也就是apk路径通过ddAssetPath()方法加入到native层的AssetManager对象的mAssetPaths中去。 但是到这里为止,却还没有发现android对resources.arsc有任何操作,不要着急,继续往下看。 在看Resources类的构造方法: public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config, CompatibilityInfo compatInfo) { mAssets = assets; mMetrics.setToDefaults(); if (compatInfo != null) { mCompatibilityInfo = compatInfo; } updateConfiguration(config, metrics); assets.ensureStringBlocks(); }Resources类的构造函数首先将参数assets所指向的一个AssetManager对象保存在成员变量mAssets中,以便以后可以通过它来访问应用程序的资源,接下来调用另外一个成员函数updateConfiguration来设置设备配置信息,最后调用参数assets所指向的一个AssetManager对象的成员函数ensureStringBlocks来创建字符串资源池。 AssetManager类的成员函数ensureStringBlocks首先检查成员变量mStringBlocks的值是否等于null。如果等于null的话,那么就说明当前应用程序使用的资源表中的资源项值字符串资源池还没有读取出来,这时候就会调用另外一个成员函数makeStringBlocks来进行读取。 整个过程大致过程如下所示: 在上图中第九步中就会处理mAssetPaths路径中apk内的resources.arsc,并把结果缓存起来。其中ResTable类负责资源管理框架中加载resources.arsc,一个ResTable可以管理app中所有的resources.arsc。 先来看java层AssetManager中的ensureStringBlocks()方法: final void ensureStringBlocks() { if (mStringBlocks == null) { synchronized (this) { if (mStringBlocks == null) { makeStringBlocks(sSystem.mStringBlocks); } } } }其中sSystem是AssetManager中的一个静态属性成员: static AssetManager sSystem = null; private StringBlock mStringBlocks[] = null;前面介绍了,zygote启动的时候,会初始化该变量,该变量指向的AssetManager对象是用来管理系统资源的。而且mStringBlocks会被初始化为系统资源中字符串值池的个数。因为系统资源为framework-res.apk,其内部只有一个resources.arsc,所以只有一个字符串资源值池。StringBlocks个数也可以理解为加载的resources.arsc的个数。 final void makeStringBlocks(StringBlock[] seed) { // 系统预加载的resources.arsc的数量 final int seedNum = (seed != null) ? seed.length : 0; // 这是个jni方法,该方法很重要 // 该方法中回去打开前面加入到native层AssetManager.mAssetPaths中的apk中的resources.arsc // 至少返回2,系统资源+app自己的资源 的resources.arsc // 返回的个数包含了seedNum final int num = getStringBlockCount(); mStringBlocks = new StringBlock[num]; if (localLOGV) Log.v(TAG, "Making string blocks for " + this + ": " + num); for (int i=0; i这里要重点分析getStringBlockCount()和getNativeStringBlock()这两个jni方法。 首先分析getStringBlockCount(): static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) { // 得到与java层AssetManager对象对应的natvie层AssetManager对象 AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { return 0; } return am->getResources().getTableCount(); }代码很简单,调用natvie层AssetManager的getResources()方法: const ResTable& AssetManager::getResources(bool required) const { const ResTable* rt = getResTable(required); return *rt; }先看看这个ResTable类中的属性成员: class ResTable { mutable Mutex mLock; status_t mError;、 // 配置相关 ResTable_config mParams; // 这个Header可以理解为resources.arsc中的资源索引表头部+字符串值池 // 也就是resources.arsc中package之前的数据 // 那么这里的mHeaders是一个数组,也就是ResTable是可以包含多个resources.arsc.(至少两个嘛) // 所有的resources.arsc的package之前的数据都存储在这数组里 VectormHeaders; // 每一个resources.arsc里面的所有Pacakge形成一个PackageGroup // 这个数组中存储ResTable中加载的所有的resources.arsc中的PackageGroup VectormPackageGroups; // package ID 对应的pacakage所在的PackageGroup 在mPackageGroups数组中的索引 uint8_t mPackageMap[256]; uint8_t mNextPackageId; }再来看看Header: struct ResTable::Header { Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL), resourceIDMap(NULL), resourceIDMapSize(0) { } ~Header() { free(resourceIDMap); } // 所在的ResTable对象 const ResTable* const owner; void* ownedData; // 资源索引表头部 const ResTable_header* header; size_t size; const uint8_t* dataEnd; size_t index; int32_t cookie; // 用来操作字符串值池 ResStringPool values; uint32_t* resourceIDMap; size_t resourceIDMapSize; };再看PackageGroup: struct ResTable::PackageGroup{ // 所在的Restable对象 const ResTable* const owner; String16 const name; uint32_t const id; // 这个resources.arsc中包含的package,一般来说只有一个 Vectorpackages; ........ } struct ResTable::Package { // 所在ResTable对象 const ResTable* const owner; // 他的header const Header* const header; // resources.arsc中的数据块起始处 const ResTable_package* const package; // 类型字符串池 ResStringPool typeStrings; // 资源项名称字符串池 ResStringPool keyStrings; size_t typeIdOffset;也就是说Restable类负责管理app要使用的所有的resouces.arsc.后续的操作都是对Restable的mHeades和mPackageGroups的操作,将解析的resoiurces.arsc相应数据分别存储在mHeades和mPackageGroups。 那么接下来看getResTable()方法,其传入的参数为true: const ResTable* AssetManager::getResTable(bool required) const { // 一个native层的AssetManager对象只包含一个ResTable对象,保存在mResources中 // 如果已经创建,那么直接返回 ResTable* rt = mResources; if (rt) { return rt; } ............ // 创建ResTable对象 mResources = new ResTable(); updateResourceParamsLocked(); bool onlyEmptyResources = true; const size_t N = mAssetPaths.size(); for (size_t i=0; i核心逻辑在appendPathToResTable()方法中,不过这里要注意一点,因为mAssetPaths包括系统资源包路径,而系统资源包已经在zygote启动时加载了,所以其reources.arsc不需要再此被加载了。 appendPathToResTable()肯定会对系统资源包做特殊处理,参数是资源包路径。 bool AssetManager::appendPathToResTable(const asset_path& ap) const { // skip those ap"s that correspond to system overlays if (ap.isSystemOverlay) { return true; } Asset* ass = NULL; ResTable* sharedRes = NULL; bool shared = true; bool onlyEmptyResources = true; MY_TRACE_BEGIN(ap.path.string()); // 资源覆盖机制,暂不考虑 Asset* idmap = openIdmapLocked(ap); size_t nextEntryIdx = mResources->getTableCount(); ALOGV("Looking for resource asset in "%s"n", ap.path.string()); // 资源包路径不是一个文件夹,那就是一个apk文件了 if (ap.type != kFileTypeDirectory) { // 对于app来说,第一次执行时,肯定为0,因为mResources刚创建,还没对其操作 // 下面的分支 指挥在参数是系统资源包路径时,才执行, // 而且系统资源包路径是首次被解析的 // 第二次执行appendPathToResTable,nextEntryIdx就不会为0了 if (nextEntryIdx == 0) { // mAssetPaths中存储的第一个资源包路径是系统资源的路径, // 即framework-res.apk的路径,它在zygote启动时已经加载了 // 可以通过mZipSet.getZipResourceTable获得其ResTable对象 sharedRes = const_cast(this)->mZipSet.getZipResourceTable(ap.path); // 对于APP来说,肯定不为NULL if (sharedRes != NULL) { // 得到系统资源包路径中resources.arsc个数 nextEntryIdx = sharedRes->getTableCount(); } } // 当参数是mAssetPaths中除第一个以外的其他资源资源包路径, // 比如app自己的资源包路径时,走下面的逻辑 if (sharedRes == NULL) { // 检查该资源包是否被其他进程加载了,这与ZipSet数据结构有关,后面在详细介绍 ass = const_cast(this)->mZipSet.getZipResourceTableAsset(ap.path); // 对于app自己的资源包来说,一般都会都下面的逻辑 if (ass == NULL) { ALOGV("loading resource table %sn", ap.path.string()); // 创建Asset对象,就是打开resources.arsc ass = const_cast(this)->openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap); if (ass != NULL && ass != kExcludedAsset) { ass = const_cast(this)->mZipSet.setZipResourceTableAsset(ap.path, ass); } } // 只有在zygote启动时,才会执行下面的逻辑 // 为系统资源创建 ResTable,并加入到mZipSet里。 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对象,并把前面与resources.arsc关联的Asset对象,加入到这个ResTabl中 sharedRes = new ResTable(); 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"); addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx); #endif sharedRes = const_cast(this)->mZipSet.setZipResourceTable(ap.path, sharedRes); } } } else { ALOGV("loading resource table %sn", ap.path.string()); ass = const_cast(this)->openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap); shared = false; } if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) { ALOGV("Installing resource asset %p in to table %pn", ass, mResources); // 系统资源包时 if (sharedRes != NULL) { ALOGV("Copying existing resources for %s", ap.path.string()); mResources->add(sharedRes); } else { // 非系统资源包时,将与resources.arsc关联的Asset对象加入到Restable中 // 此过程会解析resources.arsc文件。 ALOGV("Parsing resources for %s", ap.path.string()); mResources->add(ass, idmap, nextEntryIdx + 1, !shared); } onlyEmptyResources = false; if (!shared) { delete ass; } } else { ALOGV("Installing empty resources in to table %pn", mResources); mResources->addEmpty(nextEntryIdx + 1); } if (idmap != NULL) { delete idmap; } MY_TRACE_END(); return onlyEmptyResources; }从上述代码可以看出,实际解析resources.arsc文件的是ResTable的add()方法。 status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) { // 得到resources.arsc在mmap之后内存中的地址 const void* data = asset->getBuffer(true); ........... return addInternal(data, static_cast(asset->getLength()), idmapData, idmapSize, cookie, copyData); }解析大体过程如下: ResTable类的成员函数add在增加一个Asset对象(非系统资源包)时,会对该Asset对象所描述的resources.arsc文件的内容进行解析,结果就是得到一个系列的Package信息。每一个Package又包含了一个资源类型字符串资源池和一个资源项名称字符串资源池,以及一系列的资源类型规范数据块和一系列的资源项数据块。 还要注意的是,每一个资源包里面的所有Pacakge形成一个PackageGroup,保存ResTable对象的成员变量mPackageGroups中。 通过前面分析可知,ResTable类的成员函数add首先添加的是系统资源包,由于系统资源包已经解析过了,也就是说有Restable对象与之关联,那么这个add方法简单了,直接将系统资源的Restable对象中的mHeaders和mPackageGroups拷贝到这个新创建的Restable对象中即可。不需要解析了。 status_t ResTable::add(ResTable* src) { mError = src->mError; // 将系统资源的Restable对象中的mHeader数组中的header都拷贝出来 for (size_t i=0; imHeaders.size(); i++) { mHeaders.add(src->mHeaders[i]); } // 将系统资源的Restable对象中的mPackageGroups数组中的PackageGroup也都拷贝出来 for (size_t i=0; imPackageGroups.size(); i++) { PackageGroup* srcPg = src->mPackageGroups[i]; PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id); for (size_t j=0; jpackages.size(); j++) { pg->packages.add(srcPg->packages[j]); } 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]); } pg->dynamicRefTable.addMappings(srcPg->dynamicRefTable); pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId); mPackageGroups.add(pg); } memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap)); return mError; }那么现在再次来分析getStringBlockCount()就很简单了: static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) { // 得到与java层AssetManager对象对应的natvie层AssetManager对象 AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { return 0; } // 获得ResTable对象 // 然后调用ResTable的getTableCount() // 返回 mHeaders.size(),也就是resources.arsc的个数 // 包括系统资源对应的resources.arsc return am->getResources().getTableCount(); }再次来看makeStringBlocks: final void makeStringBlocks(StringBlock[] seed) { final int seedNum = (seed != null) ? seed.length : 0; // num包括了seedNum final int num = getStringBlockCount(); // StringBlock实际上用来操作resources.arsc的字符串值池 mStringBlocks = new StringBlock[num]; if (localLOGV) Log.v(TAG, "Making string blocks for " + this + ": " + num); for (int i=0; istatic jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, jint block) { AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { return 0; } return reinterpret_cast(am->getResources().getTableStringBlock(block)); } const ResStringPool* ResTable::getTableStringBlock(size_t index) const { return &mHeaders[index]->values; }getTableStringBlock很简单就是获得reouces.arsc的字符串池。然后用来构造StringBlock对象。 最后总结下,当Resources对象创建后,就解析好了其所需要的所有的资源包的resources.arsc,并且这些resources.arsc的所有字符串池已经被保存在了与Resources对象关联的 java层AssetManager对象的mStringBlocks数组中。关于Android 6.0时代:深入解析应用资源管理对象创建策略,的介绍到此结束,希望对大家有所帮助。
【Android 6.0时代:深入解析应用资源管理对象创建策略】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
好耶! 有关于Andriod开发的新知识点可以学习
有16位网友表示赞同!
这个版本的 Android 对 App 的资源管理有哪些变化啊?
有10位网友表示赞同!
看标题感觉是讲用代码新建资源对象的办法吧。
有20位网友表示赞同!
现在移动应用越来越复杂,高效的资源管理确实很关键。
有18位网友表示赞同!
学习了这个知识点以后,可以开发更流畅、更节省内存的游戏吗?
有16位网友表示赞同!
Android 6.0 不太常见了,能详细说说为什么要学习这个版本的内容吗?
有12位网友表示赞同!
之前一直以为 Android 的资源管理比较简单,现在看来我还需要深入了解一下啊。
有20位网友表示赞同!
我对 app 资源优化很感兴趣,这个教程应该能给我带来帮助!
有19位网友表示赞同!
希望能详细讲一讲不同的资源的类型和创建方式。
有13位网友表示赞同!
这种方法能不能提高 App 的运行速度?
有8位网友表示赞同!
感觉这个标题很有用啊,以后开发 app 可以参考一下。
有18位网友表示赞同!
希望教程能提供一些具体的代码示例,方便实践学习。
有10位网友表示赞同!
学习了这款 6.0 版本的资源管理技术,对更新版本也能起到一定的帮助吧?
有7位网友表示赞同!
安卓 App 开发总是需要不断的学习新的知识技能来跟上时代的步伐啊。
有8位网友表示赞同!
能不能用通俗易懂的话讲解一下这些复杂的概念呢?
有19位网友表示赞同!
我想做个有创意的 Android 应用,这个教程能帮我实现吗?
有5位网友表示赞同!
看来Android开发是一个非常深奥的技术领域,需要不断的学习和完善。
有18位网友表示赞同!
期待看到具体的案例分析,更加清晰地理解资源管理对象创建的方法。
有11位网友表示赞同!
感谢分享这么有用的安卓知识!
有12位网友表示赞同!