admin 管理员组文章数量: 887021
android系统资源访问机制的探讨,Android
我们知道在开发中,需要应用程序资源,如应用工程中assets和res目录下的图片,layout,values等,或者需要系统内置的资源。我们获取这些资源的入口对象都是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变量,而对该变量的赋值在init()方法中。
finalvoidinit(LoadedApk packageInfo,
IBinder activityToken, ActivityThread mainThread,
Resources container) {
mPackageInfo = packageInfo;
mResources = mPackageInfo.getResources(mainThread);
if(mResources !=null&& container !=null
&& container.getCompatibilityInfo().applicationScale !=
mResources.getCompatibilityInfo().applicationScale) {
if(DEBUG) {
Log.d(TAG, "loaded context has different scaling. Using container's"+
" compatiblity info:"+ container.getDisplayMetrics());
}
mResources = mainThread.getTopLevelResources(
mPackageInfo.getResDir(), container.getCompatibilityInfo().copy());
}
mMainThread = mainThread;
mContentResolver = newApplicationContentResolver(this, mainThread);
setActivityToken(activityToken);
}
mResources又是调用LoadedApk的getResources方法进行赋值。代码如下。
publicResources getResources(ActivityThread mainThread) {
if(mResources ==null) {
mResources = mainThread.getTopLevelResources(mResDir, this);
}
returnmResources;
}
从代码中可以看到,最终mResources的赋值是由AcitivtyThread的getTopLevelResources方法返回。代码如下
Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
ResourcesKey key = newResourcesKey(resDir, compInfo.applicationScale);
Resources r;
synchronized(mPackages) {
// Resources is app scale dependent.
if(false) {
Slog.w(TAG, "getTopLevelResources: "+ resDir +" / "
+ compInfo.applicationScale);
}
WeakReference 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);
}
returnr;
}
}
AssetManager assets = newAssetManager();
if(assets.addAssetPath(resDir) ==0) {
returnnull;
}
DisplayMetrics metrics = getDisplayMetricsLocked(false);
r = newResources(assets, metrics, getConfiguration(), compInfo);
if(false) {
Slog.i(TAG, "Created app resources "+ resDir +" "+ r +": "
+ r.getConfiguration() + " appScale="
+ r.getCompatibilityInfo().applicationScale);
}
synchronized(mPackages) {
WeakReference 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();
returnexisting;
}
// XXX need to remove entries when weak references go away
mActiveResources.put(key, newWeakReference(r));
returnr;
}
}
以上代码中,mActiveResources对象内部保存了该应用程序所使用到的所有Resources对象,其类型为Hash>,可以看出这些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对象
r =newResources(assets, metrics, getConfiguration(), compInfo);
可以看出构造一个Resources需要一个AssetManager对象,一个DisplayMetrics对象,一个Configuration对象,一个CompatibilityInfo对象,后三者传入的对象都与设备或者Android平台相关的参数,因为资源的使用与这些信息总是相关。还有一个AssetManager对象,其实它并不是访问项目中res/assets下的资源,而是访问res下所有的资源。以上代码中的addAssetPath(resDir)非常关键,它为所创建的AssetManager对象添加一个资源路径。
AssetManager类的构造函数如下:
publicAssetManager() {
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实现:
staticvoidandroid_content_AssetManager_init(JNIEnv* env, jobject clazz)
{
AssetManager* am = newAssetManager();
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代码如下:
boolAssetManager::addDefaultAssets()
{
constchar* root = getenv("ANDROID_ROOT");
LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
String8 path(root);
path.appendPath(kSystemAssets);
returnaddAssetPath(path, NULL);
}
该函数首先获取Android的根目录,getenv是一个Linux系统调用,用户同样可以使用以下终端命令获取该值。
获得根目录后,再与kSystemAssets路径进行组合,该变量的定义如下:
staticconstchar* kSystemAssets ="framework/framework-res.apk";
所以最终获得的路径文件名称为/system/framework/framework-res.apk,这正式framework对应的资源文件。
分析完了AssetManager的init方法,再来看一下ensureSystemAssets方法。
privatestaticvoidensureSystemAssets() {
synchronized(sSync) {
if(sSystem ==null) {
AssetManager system = newAssetManager(true);
system.makeStringBlocks(false);
sSystem = system;
}
}
}
该方法实际上仅在framework启动时就已经调用了,因为sSystem是一个静态的AssetManager对象,该变量在Zygote启动时已经赋值了,以后都不为空,所以该方法形同虚设。
由此可以知道,Resources对象内部的AssetManager对象除了包含应用程序本身的资源路径外,还包含了framework的资源路径,这就是为什么应用程序仅使用Resources对象就可以访问应用资源和系统资源的原因。如
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内部的整个流程如下图。
本文标签: android系统资源访问机制的探讨 Android
版权声明:本文标题:android系统资源访问机制的探讨,Android 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/free/1698506391h305011.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论