android资源加载,Android资源加载过程分析

涉及源码(Android 4.4.2):

/frameworks/base/core/java/android/app/ResourcesManager.java

/frameworks/base/core/jni/android_util_AssetManager.cpp

/core/java/android/content/res/AssetManager.java

/frameworks/base/libs/androidfw/AssetManager.cpp

我们知道,如果我们希望使用或者得到某些资源,我们通常的做法是使用Context的getXXX方法,例如:

public final Drawable getDrawable(int id) {

return getResources().getDrawable(id, getTheme());

}

可以看到它首先需要获取到Resources资源对象,从资源对象中得到想要的资源。当我们使用Context的getResources方法来获取Resources对象的时候,最终调用的是ResourcesManager的getTopLevelResources方法来获取到对应的Resources对象。

ResourcesManager采用的是单例模式,这就保证了所有的Context调用的都是同一个ResourcesManager对象的getTopLevelResources方法,所以不同Context的getResources方法获取是同一套资源对象。

/frameworks/base/core/java/android/app/ResourcesManager.java

// 单例模式获取ResourcesManager对象

public static ResourcesManager getInstance() {

synchronized (ResourcesManager.class) {

if (sResourcesManager == null) {

sResourcesManager = new ResourcesManager();

}

return sResourcesManager;

}

}

下面来看看ResourcesManager的getTopLevelResources方法

/frameworks/base/core/java/android/app/ResourcesManager.java

// 资源对象存放在ArrayMap的集合中,并且对象使用的是弱引用

final ArrayMap> mActiveResources

= new ArrayMap>();

public Resources getTopLevelResources(String resDir, int displayId,

Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {

final float scale = compatInfo.applicationScale;

// 1、通过传入的参数确定资源对象的key值

ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,

token);

Resources r;

synchronized (this) {

// 2、通过key值,从mActiveResources Map集合中获取对应的资源对象

WeakReferencewr = mActiveResources.get(key);

r = wr != null ? wr.get() : null;

// 3、如果资源对象不为空,直接将其返回,否则执行步骤4

if (r != null && r.getAssets().isUpToDate()) {

return r;

}

}

// 4、创建AssetManager对象,并且将资源目录(实际为apk文件路径)加入资源路径

AssetManager assets = new AssetManager();

if (assets.addAssetPath(resDir) == 0) {

return null;

}

DisplayMetrics dm = getDisplayMetricsLocked(displayId);

Configuration config;

boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);

final boolean hasOverrideConfig = key.hasOverrideConfiguration();

if (!isDefaultDisplay || hasOverrideConfig) {

config = new Configuration(getConfiguration());

if (!isDefaultDisplay) {

applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);

}

if (hasOverrideConfig) {

config.updateFrom(key.mOverrideConfiguration);

}

} else {

config = getConfiguration();

}

// 5、创建资源对象

r = new Resources(assets, dm, config, compatInfo, token);

// 6、如果mActiveResources Map集合没有该资源对象,则将其加入,并将资源对象返回

synchronized (this) {

WeakReferencewr = mActiveResources.get(key);

Resources existing = wr != null ? wr.get() : null;

if (existing != null && existing.getAssets().isUpToDate()) {

r.getAssets().close();

return existing;

}

mActiveResources.put(key, new WeakReference(r));

return r;

}

}

从上面可以总结以下几点:

1、Resources对象使用了一个ArrayMap对象进行缓存,因此表明其内部可能包含多个Resources对象。

2、Resources对象中包含一个AssetManager对象,资源的添加工作是通过该对象的addAssetPath完成的。

下面就来看看具体的资源资源添加的过程

AssetManager的创建

/core/java/android/content/res/AssetManager.java

public AssetManager() {

synchronized (this) {

// 1、调用init初始化方法

init();

// 2、保证系统资源对象的存在

ensureSystemAssets();

}

}

1、调用init初始化方法

init方法是一个native方法,它最终调用的是android_util_AssetManager.cpp中的android_content_AssetManager_init方法,下面就进入native层进行操作了,前面的操作都是在java层处理的。

/frameworks/base/core/jni/android_util_AssetManager.cpp

static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)

{

// 1、创建一个AssetManager对象

AssetManager* am = new AssetManager();

// 2、添加默认的资源

am->addDefaultAssets();

// 3、将AssetManager对象(C++对象)的引用保存在Java层AssetManager对象的mObject中

env->SetIntField(clazz, gAssetManagerOffsets.mObject, (jint)am);

}

(1) 添加系统默认资源操作

/frameworks/base/libs/androidfw/AssetManager.cpp

static const char* kSystemAssets = "framework/framework-res.apk";

bool AssetManager::addDefaultAssets()

{

// 1、得到系统目录/system/

const char* root = getenv("ANDROID_ROOT");

String8 path(root);

// 2、得到系统资源的完整路径/system/framework/framework-res.apk

path.appendPath(kSystemAssets);

// 3、将系统资源添加到资源路径

return addAssetPath(path, NULL);

}

从上面可以看到,默认会将系统资源添加到资源路径,这也是我们应用可以访问到系统资源的原因。

(2)添加AssetManager对象(C++对象)引用到Java层AssetManager对象的mObject上的操作

其实就是要弄清楚gAssetManagerOffsets.mObject的什么东西?

下面来看看android_util_AssetManager.cpp中register_android_content_AssetManager方法。

/frameworks/base/core/jni/android_util_AssetManager.cpp

int register_android_content_AssetManager(JNIEnv* env)

{

// 1、获取到java层的AssetManager类

jclass assetManager = env->FindClass("android/content/res/AssetManager");

// 2、获取java层的AssetManager类的mObject字段,并将其保存在gAssetManagerOffsets.mObject中

gAssetManagerOffsets.mObject

= env->GetFieldID(assetManager, "mObject", "I");

return AndroidRuntime::registerNativeMethods(env,

"android/content/res/AssetManager", gAssetManagerMethods, NELEM(gAssetManagerMethods));

}

从上面就可以知道,gAssetManagerOffsets.mObject对应就是java层的AssetManager类的mObject字段。

2、保证系统资源对象的存在

/core/java/android/content/res/AssetManager.java

private static void ensureSystemAssets() {

synchronized (sSync) {

if (sSystem == null) {

AssetManager system = new AssetManager(true);

system.makeStringBlocks(false);

sSystem = system;

}

}

}

sSystem是一个静态的AssetManager对象,在Zygote启动时已经赋值了,主要就是初次启动的时候会执行,供系统使用,上面创建AssetManager对象,创建过程跟前面相同。

上面类之间的关系如下图所示:

315c0eb3705242b2b01be9367bb64ae5.png

resourceload.png

资源路径的添加

从上图可以看到,在java层的AssetManager类中,addAssetPath方法会调用addAssetPathNative方法,addAssetPathNative方法是一个native方法,它对应的就是android_util_AssetManager.cpp中的android_content_AssetManager_addAssetPath方法。

/frameworks/base/core/jni/android_util_AssetManager.cpp

static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,

jstring path)

{

ScopedUtfChars path8(env, path);

if (path8.c_str() == NULL) {

return 0;

}

// 1、得到C++对象AssetManager对象

AssetManager* am = assetManagerForJavaObject(env, clazz);

if (am == NULL) {

return 0;

}

void* cookie;

// 2、执行addAssetPath方法

bool res = am->addAssetPath(String8(path8.c_str()), &cookie);

return (res) ? (jint)cookie : 0;

}

assetManagerForJavaObject方法就是拿到前面存放在java层的AssetManager类的mObject字段的值,它就是一个AssetManager的C++对象引用

AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj)

{

AssetManager* am = (AssetManager*)env->GetIntField(obj, gAssetManagerOffsets.mObject);

if (am != NULL) {

return am;

}

jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");

return NULL;

}

/frameworks/base/libs/androidfw/AssetManager.cpp

// 资源路径存放在一个Vector集合中

VectormAssetPaths;

// 存放资源路径的结构体

struct asset_path

{

String8 path; // 路径名

FileType type; // 文件类型

String8 idmap;

};

bool AssetManager::addAssetPath(const String8& path, void** cookie)

{

AutoMutex _l(mLock);

asset_path ap;

// 1、构造一个asset_path结构体,其实就是确定路径名和文件类型,并保存在结构体中

String8 realPath(path);

if (kAppZipName) {

realPath.appendPath(kAppZipName);

}

ap.type = ::getFileType(realPath.string());

if (ap.type == kFileTypeRegular) {

ap.path = realPath;

} else {

ap.path = path;

ap.type = ::getFileType(path.string());

if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) {

ALOGW("Asset path %s is neither a directory nor file (type=%d).",

path.string(), (int)ap.type);

return false;

}

}

// 2、如果该路径已经添加到集合中,则直接设置cookie为(索引+1)并返回,否则进入步骤3

for (size_t i=0; i

如下图所示:

e464e9a2ba7ad8f71cafe005eaba5f53.png

resourcesadd.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值