1、相关类图
这里主要说明一下,为什么Resources和AssetManager都有一个mSystem属性和getSystem()方法,这是因为我们的应用不但要使用我们自己的资源,还要使用系统资源,也就是framework-res.apk中的资源,所以这里的mSystem是用来获取系统资源的。
2、初始化过程
在Activity启动过程中了解到,当zogyte进程fork一个应用进程用于运行子进程时,会跳转到ActivityThread.main()中进行一系列操作,这其中有一步就是创建应用程序运行的上下文环境ContextImpl:
ActivityThread.ApplicationThread.handleBindApplication()
private void handleBindApplication(AppBindData data) {
//..........
// Context初始化(ContextImpl)
final ContextImpl appContext = ContextImpl.createAppContext(this/*ActivityThread*/, data.info/*LoadedApk*/);
//........
}
/**
*ActivityThread mainThread = mainThread
*LoadedApk packageInfo = packageInfo
*boolean restricted = false
*int createDisplayWithId = Display.INVALID_DISPLAY
*/
private ContextImpl(ContextImpl container, ActivityThread mainThread,
LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
Display display, Configuration overrideConfiguration, int createDisplayWithId) {
//.......
// LoadedApk赋值
mPackageInfo = packageInfo;
mResourcesManager = ResourcesManager.getInstance();
// resources初始化:通过LoadedApk.getResources来创建一个Resources实例
Resources resources = packageInfo.getResources(mainThread);
mResources = resources;// 赋值
//......
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
}
LoadedApk.getResources()
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
// ActivityThread.getTopLevelResources()
mResources = mainThread.getTopLevelResources(mResDir/*APK文件位置*/, mSplitResDirs, mOverlayDirs,
mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
}
return mResources;
}
ActivityThread.getTopLevelResources()
/**
* Creates the top level resources for the given package.
*/
Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
String[] libDirs, int displayId, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo());
}
ResourcesManager.getTopLevelResources
/**
* Creates the top level Resources for applications with the given compatibility info.
*
* @param resDir the resource directory.
* @param splitResDirs split resource directories.
* @param overlayDirs the resource overlay directories.
* @param libDirs the shared library resource dirs this app references.
* @param displayId display Id.
* @param overrideConfiguration override configurations.
* @param compatInfo the compatibility info. Must not be null.
*/
Resources getTopLevelResources(String resDir, String[] splitResDirs,
String[] overlayDirs, String[] libDirs, int displayId,
Configuration overrideConfiguration, CompatibilityInfo compatInfo) {
final float scale = compatInfo.applicationScale;
Configuration overrideConfigCopy = (overrideConfiguration != null)
? new Configuration(overrideConfiguration) : null;
ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
Resources r;
synchronized (this) {
// Resources is app scale dependent.
if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
// Resources是以ResourcesKey为key以弱应用的方式保存在mActiveResources这个Map中
WeakReference wr = mActiveResources.get(key);
r = wr != null ? wr.get() : null;
//if (r != null) Log.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
if (r != null && r.getAssets().isUpToDate()) {/
// 缓存里面有,并且是最新的
if (DEBUG) Slog.w(TAG, "Returning cached resources " + r + " " + resDir
+ ": appScale=" + r.getCompatibilityInfo().applicationScale
+ " key=" + key + " overrideConfig=" + overrideConfiguration);
return r;
}
}
//if (r != null) {
// Log.w(TAG, "Throwing away out-of-date resources!!!! "
// + r + " " + resDir);
//}
// AssetManager创建
AssetManager assets = new AssetManager();
// resDir can be null if the 'android' package is creating a new Resources object.
// This is fine, since each AssetManager automatically loads the 'android' package
// already.
if (resDir != null) {
if (assets.addAssetPath(resDir) == 0) {
return null;
}
}
if (splitResDirs != null) {
for (String splitResDir : splitResDirs) {
if (assets.addAssetPath(splitResDir) == 0) {
return null;
}
}
}
if (overlayDirs != null) {
for (String idmapPath : overlayDirs) {
assets.addOverlayPath(idmapPath);
}
}
if (libDirs != null) {
for (String libDir : libDirs) {
if (libDir.endsWith(".apk")) {
// Avoid opening files we know do not have resources,
// like code-only .jar files.
if (assets.addAssetPath(libDir) == 0) {
Log.w(TAG, "Asset path '" + libDir +
"' does not exist or contains no resources.");
}
}
}
}
//Log.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
DisplayMetrics dm = getDisplayMetricsLocked(displayId);
Configuration config;
final 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);
if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
}
} else {
config = getConfiguration();
}
// 创建Resources
r = new Resources(assets, dm, config, compatInfo);
if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale);
synchronized (this) {
// 可能其他线程已经创建好了,则直接返回
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();
return existing;
}
// XXX need to remove entries when weak references go away
// 把最新的对象保存到缓存中
mActiveResources.put(key, new WeakReference<>(r));
if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size());
return r;
}
}
过程是:先从缓存中取,如果缓存中有且没有过时,则直接返回,否则依次创建AssetManager 和Resources
AssetManager构造函数
/**
* Create a new AssetManager containing only the basic system assets.
* Applications will not generally use this method, instead retrieving the
* appropriate asset manager with {@link Resources#getAssets}. Not for
* use by applications.
* {@hide}
*/
public AssetManager() {
synchronized (this) {
//......
init(false);
// 确保有能够访问系统资源的AssetManager对象
ensureSystemAssets();
}
}
android_util_AssetManager.android_content_AssetManager_init()
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
if (isSystem) {// false
verifySystemIdmaps();
}
AssetManager* am = new AssetManager();
if (am == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", "");
return;
}
am->addDefaultAssets();
ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am));
}
AssetManager.cpp:addDefaultAssets()
bool AssetManager::addDefaultAssets()
{
// root = /system/
const char* root = getenv("ANDROID_ROOT");
LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
String8 path(root);
// path = /system/framework/framework-res.apk
path.appendPath(kSystemAssets);
return addAssetPath(path, NULL);
}
bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
{
AutoMutex _l(mLock);
asset_path ap;
String8 realPath(path);
if (kAppZipName) {
// 如果kAppZipName不为NULL(classes.jar),这里这个值是为NULL的
realPath.appendPath(kAppZipName);
}
ap.type = ::getFileType(realPath.string());
if (ap.type == kFileTypeRegular) {// kAppZipName不为NULL
ap.path = realPath;
} else {
// kAppZipName为NULL
ap.path = path;//ap.path指向APK文件
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;
}
}
// Skip if we have it already.
for (size_t i=0; i(i+1);
}
return true;
}
}
ALOGV("In %p Asset %s path: %s", this,
ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());
// Check that the path has an AndroidManifest.xml
Asset* manifestAsset = const_cast(this)->openNonAssetInPathLocked(
kAndroidManifest, Asset::ACCESS_BUFFER, ap);
if (manifestAsset == NULL) {
// This asset path does not contain any resources.
delete manifestAsset;
return false;
}
delete manifestAsset;
mAssetPaths.add(ap);
// new paths are always added at the end
if (cookie) {
*cookie = static_cast(mAssetPaths.size());
}
#ifdef __ANDROID__
// Load overlays, if any
asset_path oap;
for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
mAssetPaths.add(oap);
}
#endif
if (mResources != NULL) {
appendPathToResTable(ap);
}
return true;
}
以上,是AssetManager.java构造函数的第一步:init(false),其主要工作是加载系统资源(framework-res.apk),以供后续应用程序使用。而其第二部:ensureSystemAssets()也是为了创建系统资源使用对象AssetManager
private static void ensureSystemAssets() {
synchronized (sSync) {
if (sSystem == null) {
AssetManager system = new AssetManager(true);
system.makeStringBlocks(null);
sSystem = system;
}
}
}
初始化了AssetManager之后,就可以通过AssetManager.addAssetPath()加载本身资源了.
在AssetManager加载完相关资源后,就可以创建Resources了:
/**
* Creates a new Resources object with CompatibilityInfo.
*
* @param assets Previously created AssetManager.
* @param metrics Current display metrics to consider when
* selecting/computing resource values.
* 设备分辨率相关信息:屏幕分辨率,density,font scaling
* @param config Desired device configuration to consider when
* selecting/computing resource values (optional).
* 该配置信息用来决定使用哪套资源
* @param compatInfo this resource's compatibility info. Must not be null.
* 资源兼容性信息
* @hide
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
CompatibilityInfo compatInfo) {
mAssets = assets;
mMetrics.setToDefaults();
if (compatInfo != null) {
mCompatibilityInfo = compatInfo;
}
// 设备相关配置信息更新处理
updateConfiguration(config, metrics);
// 创建字符串资源池
assets.ensureStringBlocks();
}