Android资源访问机制—获取Resources对象
2015-08-26 17:19
651 查看
前人指路:http://willsunforjava.iteye.com/blog/1663355
modify base on android-4.4.4_r2.0.1
我们知道在开发中,需要应用程序资源,如应用工程中assets和res目录下的图片,layout,values等,或者需要系统内置的资源。我们获取这些资源的入口对象都是Resources对象,并博文将分析如何获取Resources对象。
获取Resources的过程:
(1)将framework/framework-res.apk和应用资源apk装载为Resources对象。
(2)获取Resources对象
获取Resources对象有两种方式,第一种通过Context,第二种通过PackageManager。
1. 通过Context获取Resources对象
在一个Acitvity或者一个Service中,我们直接this.getResources()方法,就可以获得Reousrces对象。其实Acitivity或者Service本质上就是一个Context.getResources()方法来自Context,而真正实现Context接口是ContextImpl类,所以调用的实际上时ContextImpl类的getResources()方法。
我们查看ContextImpl类源码可以看到,getResources方法直接返回内部的mResources变量,而对该变量的赋值在私有的构造方法中。
core/java/android/app/ContextImpl.java
mResources又是调用LoadedApk的getResources方法进行赋值。代码如下。
Java代码
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
mResources = mainThread.getTopLevelResources(mResDir, this);
}
return mResources;
}
从代码中可以看到,最终mResources的赋值是由AcitivtyThread的getTopLevelResources方法返回。代码如下。
Java代码
Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);
Resources r;
synchronized (mPackages) {
// Resources is app scale dependent.
if (false) {
Slog.w(TAG, "getTopLevelResources: " + resDir + " / "
+ compInfo.applicationScale);
}
WeakReference<Resources> wr = mActiveResources.get(key);
r = wr != null ? wr.get() : null;
if (r != null && r.getAssets().isUpToDate()) {
if (false) {
Slog.w(TAG, "Returning cached resources " + r + " " + resDir
+ ": appScale=" + r.getCompatibilityInfo().applicationScale);
}
return r;
}
}
AssetManager assets = new AssetManager();
if (assets.addAssetPath(resDir) == 0) {
return null;
}
DisplayMetrics metrics = getDisplayMetricsLocked(false);
r = new Resources(assets, metrics, getConfiguration(), compInfo);
if (false) {
Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ r.getConfiguration() + " appScale="
+ r.getCompatibilityInfo().applicationScale);
}
synchronized (mPackages) {
WeakReference<Resources> wr = mActiveResources.get(key);
Resources existing = wr != null ? wr.get() : null;
if (existing != null && existing.getAssets().isUpToDate()) {
// Someone else already created the resources while we were
// unlocked; go ahead and use theirs.
r.getAssets().close();
return existing;
}
// XXX need to remove entries when weak references go away
mActiveResources.put(key, new WeakReference<Resources>(r));
return r;
}
}
以上代码中,mActiveResources对象内部保存了该应用程序所使用到的所有Resources对象,其类型为Hash<ResourcesKey,WeakReference<Resourcces>>,可以看出这些Resources对象都是以一个弱引用的方式保存,以便在内存紧张时可以释放Resources所占内存。
ResourcesKey的构造需要resDir和compInfo.applicationScale。resdDir变量的含义是资源文件所在路径,实际指的是APK程序所在路径,比如可以是:/data/app/com.haii.android.xxx-1.apk,该apk会对应/data/dalvik-cache目录下的:data@app@com.haii.android.xxx-1.apk@classes.dex文件。
所以,如果一个应用程序没有访问该程序以外的资源,那么mActiveResources变量中就仅有一个Resources对象。这也从侧面说明,mActiveResources内部可能包含多个Resources对象,条件是必须有不同的ResourceKey,也就是必须有不同的resDir,这就意味着一个应用程序可以访问另外的APK文件,并从中读取读取其资源。(PS:其实目前的“换肤”就是采用加载不同的资源apk实现主题切换的)
如果mActivityResources对象中没有包含所要的Resources对象,那么,就重新建立一个Resources对象
Java代码
r = new Resources(assets, metrics, getConfiguration(), compInfo);
可以看出构造一个Resources需要一个AssetManager对象,一个DisplayMetrics对象,一个Configuration对象,一个CompatibilityInfo对象,后三者传入的对象都与设备或者Android平台相关的参数,因为资源的使用与这些信息总是相关。还有一个AssetManager对象,其实它并不是访问项目中res/assets下的资源,而是访问res下所有的资源。以上代码中的addAssetPath(resDir)非常关键,它为所创建的AssetManager对象添加一个资源路径。
AssetManager类的构造函数如下:
Java代码
public AssetManager() {
synchronized (this) {
if (DEBUG_REFS) {
mNumRefs = 0;
incRefsLocked(this.hashCode());
}
init();
if (localLOGV) Log.v(TAG, "New asset manager: " + this);
ensureSystemAssets();
}
}
构造方法中调用两个方法init()和ensureSystemAssets(),init方法是一个native实现。AssetManager.java对应的C++文件是android_util_AssetManager.cpp(注意不是AssetManager.cpp,它是C++层内部使用的cpp文件,与Java层无关)。下面看一下init()的native实现。
Cpp代码
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)
{
AssetManager* am = new AssetManager();
if (am == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", "");
return;
}
am->addDefaultAssets();
LOGV("Created AssetManager %p for Java object %p\n", am, clazz);
env->SetIntField(clazz, gAssetManagerOffsets.mObject, (jint)am);
}
首先创建一个C++类的AssetManager对象,然后调用am->addDefaultAssets()方法,该方法的作用就是把framework的资源文件添加到这个AssetManager对象的路径中。最后调用setInitField()方法把C++创建的AssetManager对象的引用保存到Java端的mObject变量中,这种方式是常用的C++层与Java层通信的方式。
addDefaultAssets代码如下:
Java代码
bool AssetManager::addDefaultAssets()
{
const char* root = getenv("ANDROID_ROOT");
LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
String8 path(root);
path.appendPath(kSystemAssets);
return addAssetPath(path, NULL);
}
该函数首先获取Android的根目录,getenv是一个Linux系统调用,用户同样可以使用以下终端命令获取该值。
获得根目录后,再与kSystemAssets路径进行组合,该变量的定义如下:
Cpp代码
static const char* kSystemAssets = "framework/framework-res.apk";
所以最终获得的路径文件名称为/system/framework/framework-res.apk,这正式framework对应的资源文件。
分析完了AssetManager的init方法,再来看一下ensureSystemAssets方法。
Java代码
private static void ensureSystemAssets() {
synchronized (sSync) {
if (sSystem == null) {
AssetManager system = new AssetManager(true);
system.makeStringBlocks(false);
sSystem = system;
}
}
}
该方法实际上仅在framework启动时就已经调用了,因为sSystem是一个静态的AssetManager对象,该变量在Zygote启动时已经赋值了,以后都不为空,所以该方法形同虚设。
由此可以知道,Resources对象内部的AssetManager对象除了包含应用程序本身的资源路径外,还包含了framework的资源路径,这就是为什么应用程序仅使用Resources对象就可以访问应用资源和系统资源的原因。如
Java代码
Resources res = getResources();
Drawable btnPic = res.getDrawable(android.R.drawable.btn_default_small);
那么如何AssetManager如何区分访问的是系统资源还是应用资源呢?当使用getXXX(int id)访问资源时,如果id值小于0x10000000时,AssetManager会认为要访问的是系统资源。因为aapt在对系统资源进行编译时,所有的资源id都被编译为小于该值的一个int值,而当访问应用资源时,id值都大于0x70000000。
创建好了Resources对象后,就把该变量缓存到mActiveResources中,以便以后继续使用。
访问Resources内部的整个流程如下图。
2. 通过PackageManager获取Resources对象
文件路径/frameworks/base/+/android-4.4.4_r2.0.1/core/java/android/content/pm/PackageManager.java
packageManager为抽象类,,跟Resource相关的方法有:
具体的实现类为ApplicationPackageManager.java
1.包名为system的情况,mContext.mMainThread.getSystemContext().getResources()
ActivityThread.getSystemContext
ContextImpl.createSystemContext
最终的mResource是通过packageinfo.getResources和mResoucesManager.getTopLevelResources得到
packageInfo为core/java/android/app/LoadedApk.java
core/java/android/app/ResourcesManager.java
这里我们可以看到创建的Resouces保存到ResourcesManager中的mActiveResources中,相同的包名存对应的key,存在对应的Resources
2.通用情况,mContext.mMainThread.getTopLevelResources
结果同样通过ResourcesManager.getTopLevelResources来获取Resources
结论:
1.获取Resources,最终都是通过ResourcesManager获取
2.ResourcesManager中使用
ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources保存Resources的键值对
在内存不足是,弱引用会被回收
PS:
在Resources被回收后,会重新创建Resources对象
->Activity.getResources
->ContextThemeWrapper.getResources
->ContextWrapper.createConfigurationContext
->ContextImpl.createConfigurationContext
ContextThemeWrapper.getResources
ContextWrapper.createConfigurationContext
ContextImpl.createConfigurationContext
最后调用ContextImpl的构造方法创建Context
modify base on android-4.4.4_r2.0.1
我们知道在开发中,需要应用程序资源,如应用工程中assets和res目录下的图片,layout,values等,或者需要系统内置的资源。我们获取这些资源的入口对象都是Resources对象,并博文将分析如何获取Resources对象。
获取Resources的过程:
(1)将framework/framework-res.apk和应用资源apk装载为Resources对象。
(2)获取Resources对象
获取Resources对象有两种方式,第一种通过Context,第二种通过PackageManager。
1. 通过Context获取Resources对象
在一个Acitvity或者一个Service中,我们直接this.getResources()方法,就可以获得Reousrces对象。其实Acitivity或者Service本质上就是一个Context.getResources()方法来自Context,而真正实现Context接口是ContextImpl类,所以调用的实际上时ContextImpl类的getResources()方法。
我们查看ContextImpl类源码可以看到,getResources方法直接返回内部的mResources变量,而对该变量的赋值在私有的构造方法中。
core/java/android/app/ContextImpl.java
private ContextImpl(ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration) { mOuterContext = this; mMainThread = mainThread; mActivityToken = activityToken; mRestricted = restricted; if (user == null) { user = Process.myUserHandle(); } mUser = user; mPackageInfo = packageInfo; mContentResolver = new ApplicationContentResolver(this, mainThread, user); mResourcesManager = ResourcesManager.getInstance(); mDisplay = display; mOverrideConfiguration = overrideConfiguration; final int displayId = getDisplayId(); CompatibilityInfo compatInfo = null; if (container != null) { compatInfo = container.getDisplayAdjustments(displayId).getCompatibilityInfo(); } if (compatInfo == null && displayId == Display.DEFAULT_DISPLAY) { compatInfo = packageInfo.getCompatibilityInfo(); } mDisplayAdjustments.setCompatibilityInfo(compatInfo); mDisplayAdjustments.setActivityToken(activityToken); Resources resources = packageInfo.getResources(mainThread); if (resources != null) { if (activityToken != null || displayId != Display.DEFAULT_DISPLAY || overrideConfiguration != null || (compatInfo != null && compatInfo.applicationScale != resources.getCompatibilityInfo().applicationScale)) { resources = mResourcesManager.getTopLevelResources( packageInfo.getResDir(), displayId, overrideConfiguration, compatInfo, activityToken); } } mResources = resources; if (container != null) { mBasePackageName = container.mBasePackageName; mOpPackageName = container.mOpPackageName; } else { mBasePackageName = packageInfo.mPackageName; ApplicationInfo ainfo = packageInfo.getApplicationInfo(); if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) { // Special case: system components allow themselves to be loaded in to other // processes. For purposes of app ops, we must then consider the context as // belonging to the package of this process, not the system itself, otherwise // the package+uid verifications in app ops will fail. mOpPackageName = ActivityThread.currentPackageName(); } else { mOpPackageName = mBasePackageName; } } }
mResources又是调用LoadedApk的getResources方法进行赋值。代码如下。
Java代码
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
mResources = mainThread.getTopLevelResources(mResDir, this);
}
return mResources;
}
从代码中可以看到,最终mResources的赋值是由AcitivtyThread的getTopLevelResources方法返回。代码如下。
Java代码
Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);
Resources r;
synchronized (mPackages) {
// Resources is app scale dependent.
if (false) {
Slog.w(TAG, "getTopLevelResources: " + resDir + " / "
+ compInfo.applicationScale);
}
WeakReference<Resources> wr = mActiveResources.get(key);
r = wr != null ? wr.get() : null;
if (r != null && r.getAssets().isUpToDate()) {
if (false) {
Slog.w(TAG, "Returning cached resources " + r + " " + resDir
+ ": appScale=" + r.getCompatibilityInfo().applicationScale);
}
return r;
}
}
AssetManager assets = new AssetManager();
if (assets.addAssetPath(resDir) == 0) {
return null;
}
DisplayMetrics metrics = getDisplayMetricsLocked(false);
r = new Resources(assets, metrics, getConfiguration(), compInfo);
if (false) {
Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ r.getConfiguration() + " appScale="
+ r.getCompatibilityInfo().applicationScale);
}
synchronized (mPackages) {
WeakReference<Resources> wr = mActiveResources.get(key);
Resources existing = wr != null ? wr.get() : null;
if (existing != null && existing.getAssets().isUpToDate()) {
// Someone else already created the resources while we were
// unlocked; go ahead and use theirs.
r.getAssets().close();
return existing;
}
// XXX need to remove entries when weak references go away
mActiveResources.put(key, new WeakReference<Resources>(r));
return r;
}
}
以上代码中,mActiveResources对象内部保存了该应用程序所使用到的所有Resources对象,其类型为Hash<ResourcesKey,WeakReference<Resourcces>>,可以看出这些Resources对象都是以一个弱引用的方式保存,以便在内存紧张时可以释放Resources所占内存。
ResourcesKey的构造需要resDir和compInfo.applicationScale。resdDir变量的含义是资源文件所在路径,实际指的是APK程序所在路径,比如可以是:/data/app/com.haii.android.xxx-1.apk,该apk会对应/data/dalvik-cache目录下的:data@app@com.haii.android.xxx-1.apk@classes.dex文件。
所以,如果一个应用程序没有访问该程序以外的资源,那么mActiveResources变量中就仅有一个Resources对象。这也从侧面说明,mActiveResources内部可能包含多个Resources对象,条件是必须有不同的ResourceKey,也就是必须有不同的resDir,这就意味着一个应用程序可以访问另外的APK文件,并从中读取读取其资源。(PS:其实目前的“换肤”就是采用加载不同的资源apk实现主题切换的)
如果mActivityResources对象中没有包含所要的Resources对象,那么,就重新建立一个Resources对象
Java代码
r = new Resources(assets, metrics, getConfiguration(), compInfo);
可以看出构造一个Resources需要一个AssetManager对象,一个DisplayMetrics对象,一个Configuration对象,一个CompatibilityInfo对象,后三者传入的对象都与设备或者Android平台相关的参数,因为资源的使用与这些信息总是相关。还有一个AssetManager对象,其实它并不是访问项目中res/assets下的资源,而是访问res下所有的资源。以上代码中的addAssetPath(resDir)非常关键,它为所创建的AssetManager对象添加一个资源路径。
AssetManager类的构造函数如下:
Java代码
public AssetManager() {
synchronized (this) {
if (DEBUG_REFS) {
mNumRefs = 0;
incRefsLocked(this.hashCode());
}
init();
if (localLOGV) Log.v(TAG, "New asset manager: " + this);
ensureSystemAssets();
}
}
构造方法中调用两个方法init()和ensureSystemAssets(),init方法是一个native实现。AssetManager.java对应的C++文件是android_util_AssetManager.cpp(注意不是AssetManager.cpp,它是C++层内部使用的cpp文件,与Java层无关)。下面看一下init()的native实现。
Cpp代码
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)
{
AssetManager* am = new AssetManager();
if (am == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", "");
return;
}
am->addDefaultAssets();
LOGV("Created AssetManager %p for Java object %p\n", am, clazz);
env->SetIntField(clazz, gAssetManagerOffsets.mObject, (jint)am);
}
首先创建一个C++类的AssetManager对象,然后调用am->addDefaultAssets()方法,该方法的作用就是把framework的资源文件添加到这个AssetManager对象的路径中。最后调用setInitField()方法把C++创建的AssetManager对象的引用保存到Java端的mObject变量中,这种方式是常用的C++层与Java层通信的方式。
addDefaultAssets代码如下:
Java代码
bool AssetManager::addDefaultAssets()
{
const char* root = getenv("ANDROID_ROOT");
LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
String8 path(root);
path.appendPath(kSystemAssets);
return addAssetPath(path, NULL);
}
该函数首先获取Android的根目录,getenv是一个Linux系统调用,用户同样可以使用以下终端命令获取该值。
获得根目录后,再与kSystemAssets路径进行组合,该变量的定义如下:
Cpp代码
static const char* kSystemAssets = "framework/framework-res.apk";
所以最终获得的路径文件名称为/system/framework/framework-res.apk,这正式framework对应的资源文件。
分析完了AssetManager的init方法,再来看一下ensureSystemAssets方法。
Java代码
private static void ensureSystemAssets() {
synchronized (sSync) {
if (sSystem == null) {
AssetManager system = new AssetManager(true);
system.makeStringBlocks(false);
sSystem = system;
}
}
}
该方法实际上仅在framework启动时就已经调用了,因为sSystem是一个静态的AssetManager对象,该变量在Zygote启动时已经赋值了,以后都不为空,所以该方法形同虚设。
由此可以知道,Resources对象内部的AssetManager对象除了包含应用程序本身的资源路径外,还包含了framework的资源路径,这就是为什么应用程序仅使用Resources对象就可以访问应用资源和系统资源的原因。如
Java代码
Resources res = getResources();
Drawable btnPic = res.getDrawable(android.R.drawable.btn_default_small);
那么如何AssetManager如何区分访问的是系统资源还是应用资源呢?当使用getXXX(int id)访问资源时,如果id值小于0x10000000时,AssetManager会认为要访问的是系统资源。因为aapt在对系统资源进行编译时,所有的资源id都被编译为小于该值的一个int值,而当访问应用资源时,id值都大于0x70000000。
创建好了Resources对象后,就把该变量缓存到mActiveResources中,以便以后继续使用。
访问Resources内部的整个流程如下图。
2. 通过PackageManager获取Resources对象
文件路径/frameworks/base/+/android-4.4.4_r2.0.1/core/java/android/content/pm/PackageManager.java
packageManager为抽象类,,跟Resource相关的方法有:
public abstract Resources getResourcesForActivity(ComponentName activityName) throws NameNotFoundException; public abstract Resources getResourcesForApplication(ApplicationInfo app) public abstract Resources getResourcesForApplication(String appPackageName) throws NameNotFoundException; /** @hide */ public abstract Resources getResourcesForApplicationAsUser(String appPackageName, int userId) throws NameNotFoundException; throws NameNotFoundException;
具体的实现类为ApplicationPackageManager.java
@Override public Resources getResourcesForActivity( ComponentName activityName) throws NameNotFoundException { return getResourcesForApplication( getActivityInfo(activityName, 0).applicationInfo); } @Override public Resources getResourcesForApplication( ApplicationInfo app) throws NameNotFoundException { if (app.packageName.equals("system")) { return mContext.mMainThread.getSystemContext().getResources(); } Resources r = mContext.mMainThread.getTopLevelResources( app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo); if (r != null) { return r; } throw new NameNotFoundException("Unable to open " + app.publicSourceDir); } @Override public Resources getResourcesForApplication( String appPackageName) throws NameNotFoundException { return getResourcesForApplication( getApplicationInfo(appPackageName, 0)); } /** @hide */ @Override public Resources getResourcesForApplicationAsUser(String appPackageName, int userId) throws NameNotFoundException { if (userId < 0) { throw new IllegalArgumentException( "Call does not support special user #" + userId); } if ("system".equals(appPackageName)) { return mContext.mMainThread.getSystemContext().getResources(); } try { ApplicationInfo ai = mPM.getApplicationInfo(appPackageName, 0, userId); if (ai != null) { return getResourcesForApplication(ai); } } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } throw new NameNotFoundException("Package " + appPackageName + " doesn't exist"); }
1.包名为system的情况,mContext.mMainThread.getSystemContext().getResources()
ActivityThread.getSystemContext
public ContextImpl getSystemContext() { synchronized (this) { if (mSystemContext == null) { mSystemContext = ContextImpl.createSystemContext(this); } return mSystemContext; } }
ContextImpl.createSystemContext
static ContextImpl createSystemContext(ActivityThread mainThread) { LoadedApk packageInfo = new LoadedApk(mainThread); ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, false, null, null); context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), context.mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY)); return context; } static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); return new ContextImpl(null, mainThread, packageInfo, null, null, false, null, null); } static ContextImpl createActivityContext(ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); if (activityToken == null) throw new IllegalArgumentException("activityInfo"); return new ContextImpl(null, mainThread, packageInfo, activityToken, null, false, null, null); } private ContextImpl(ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration) { mOuterContext = this; mMainThread = mainThread; mActivityToken = activityToken; mRestricted = restricted; if (user == null) { user = Process.myUserHandle(); } mUser = user; mPackageInfo = packageInfo; mContentResolver = new ApplicationContentResolver(this, mainThread, user); mResourcesManager = ResourcesManager.getInstance(); mDisplay = display; mOverrideConfiguration = overrideConfiguration; final int displayId = getDisplayId(); CompatibilityInfo compatInfo = null; if (container != null) { compatInfo = container.getDisplayAdjustments(displayId).getCompatibilityInfo(); } if (compatInfo == null && displayId == Display.DEFAULT_DISPLAY) { compatInfo = packageInfo.getCompatibilityInfo(); } mDisplayAdjustments.setCompatibilityInfo(compatInfo); mDisplayAdjustments.setActivityToken(activityToken); Resources resources = packageInfo.getResources(mainThread); if (resources != null) { if (activityToken != null || displayId != Display.DEFAULT_DISPLAY || overrideConfiguration != null || (compatInfo != null && compatInfo.applicationScale != resources.getCompatibilityInfo().applicationScale)) { resources = mResourcesManager.getTopLevelResources( packageInfo.getResDir(), displayId, overrideConfiguration, compatInfo, activityToken); } } mResources = resources; if (container != null) { mBasePackageName = container.mBasePackageName; mOpPackageName = container.mOpPackageName; } else { mBasePackageName = packageInfo.mPackageName; ApplicationInfo ainfo = packageInfo.getApplicationInfo(); if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) { // Special case: system components allow themselves to be loaded in to other // processes. For purposes of app ops, we must then consider the context as // belonging to the package of this process, not the system itself, otherwise // the package+uid verifications in app ops will fail. mOpPackageName = ActivityThread.currentPackageName(); } else { mOpPackageName = mBasePackageName; } } }
最终的mResource是通过packageinfo.getResources和mResoucesManager.getTopLevelResources得到
packageInfo为core/java/android/app/LoadedApk.java
public Resources getResources(ActivityThread mainThread) { if (mResources == null) { mResources = mainThread.getTopLevelResources(mResDir, Display.DEFAULT_DISPLAY, null, this); } return mResources; }
core/java/android/app/ResourcesManager.java
/** * Creates the top level Resources for applications with the given compatibility info. * * @param resDir the resource directory. * @param compatInfo the compability info. Must not be null. * @param token the application token for determining stack bounds. */ public Resources getTopLevelResources(String resDir, int displayId, Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) { final float scale = compatInfo.applicationScale; ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, token); Resources r; synchronized (this) { // Resources is app scale dependent. if (false) { Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale); } WeakReference<Resources> wr = mActiveResources.get(key); r = wr != null ? wr.get() : null; //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate()); if (r != null && r.getAssets().isUpToDate()) { if (false) { Slog.w(TAG, "Returning cached resources " + r + " " + resDir + ": appScale=" + r.getCompatibilityInfo().applicationScale); } return r; } } //if (r != null) { // Slog.w(TAG, "Throwing away out-of-date resources!!!! " // + r + " " + resDir); //} AssetManager assets = new AssetManager(); if (assets.addAssetPath(resDir) == 0) { return null; } //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics); 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(); } r = new Resources(assets, dm, config, compatInfo, token); if (false) { Slog.i(TAG, "Created app resources " + resDir + " " + r + ": " + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale); } synchronized (this) { WeakReference<Resources> wr = mActiveResources.get(key); Resources existing = wr != null ? wr.get() : null; if (existing != null && existing.getAssets().isUpToDate()) { // Someone else already created the resources while we were // unlocked; go ahead and use theirs. r.getAssets().close(); return existing; } // XXX need to remove entries when weak references go away mActiveResources.put(key, new WeakReference<Resources>(r)); return r; } }
这里我们可以看到创建的Resouces保存到ResourcesManager中的mActiveResources中,相同的包名存对应的key,存在对应的Resources
final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources = new ArrayMap<ResourcesKey, WeakReference<Resources> >();
2.通用情况,mContext.mMainThread.getTopLevelResources
<pre name="code" class="java"> /** * Creates the top level resources for the given package. */ Resources getTopLevelResources(String resDir, int displayId, Configuration overrideConfiguration, LoadedApk pkgInfo) { return mResourcesManager.getTopLevelResources(resDir, displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null); }
结果同样通过ResourcesManager.getTopLevelResources来获取Resources
结论:
1.获取Resources,最终都是通过ResourcesManager获取
2.ResourcesManager中使用
ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources保存Resources的键值对
在内存不足是,弱引用会被回收
PS:
在Resources被回收后,会重新创建Resources对象
->Activity.getResources
->ContextThemeWrapper.getResources
->ContextWrapper.createConfigurationContext
->ContextImpl.createConfigurationContext
ContextThemeWrapper.getResources
public Resources getResources() { if (mResources != null) { return mResources; } if (mOverrideConfiguration == null) { mResources = super.getResources(); return mResources; } else { Context resc = createConfigurationContext(mOverrideConfiguration); mResources = resc.getResources(); return mResources; } }
ContextWrapper.createConfigurationContext
public Context createConfigurationContext(Configuration overrideConfiguration) { return mBase.createConfigurationContext(overrideConfiguration); }
ContextImpl.createConfigurationContext
public Context createConfigurationContext(Configuration overrideConfiguration) { if (overrideConfiguration == null) { throw new IllegalArgumentException("overrideConfiguration must not be null"); } return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, mRestricted, mDisplay, overrideConfiguration); }
最后调用ContextImpl的构造方法创建Context
相关文章推荐
- android去掉顶部标题栏
- Android framework系统默认设置修改
- Android三句代码使用沉浸式状态栏
- Android 获取指定城市的天气实况
- Android-三种菜单栏实现(OptionMenu,SubMenu,ContextMenu)
- Android listview中使用button解决方法 <问题记录>
- Android自定义弹性ScrollView(有误请略过此文章)
- [Android]GreenDao(2)——使用
- android
- android 用java布局一个控件
- android 截屏
- Android-实现简单照相与摄像
- android stack error message is Fail to start the plugin
- android 编译FFmpeg
- Android ble 蓝牙4.0总结——基础版
- Android收起通知栏
- Android 系统自带输入法的表情显示 -项目总结
- Android-服务与广播接收者实现本地音乐播放
- 初学Android JNI
- android intent和intent action大全,Broadcast Action大全