1性芬、相關(guān)類圖
這里主要說(shuō)明一下换团,為什么Resources和AssetManager都有一個(gè)mSystem屬性和getSystem()方法,這是因?yàn)槲覀兊膽?yīng)用不但要使用我們自己的資源,還要使用系統(tǒng)資源柠辞,也就是framework-res.apk中的資源尘颓,所以這里的mSystem是用來(lái)獲取系統(tǒng)資源的走触。
2、初始化過(guò)程
在Activity啟動(dòng)過(guò)程中了解到疤苹,當(dāng)zogyte進(jìn)程fork一個(gè)應(yīng)用進(jìn)程用于運(yùn)行子進(jìn)程時(shí)互广,會(huì)跳轉(zhuǎn)到ActivityThread.main()中進(jìn)行一系列操作,這其中有一步就是創(chuàng)建應(yīng)用程序運(yùn)行的上下文環(huán)境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初始化:通過(guò)LoadedApk.getResources來(lái)創(chuàng)建一個(gè)Resources實(shí)例
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以弱應(yīng)用的方式保存在mActiveResources這個(gè)Map中
WeakReference<Resources> 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創(chuàng)建
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();
}
// 創(chuàng)建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) {
// 可能其他線程已經(jīng)創(chuàng)建好了惫皱,則直接返回
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
// 把最新的對(duì)象保存到緩存中
mActiveResources.put(key, new WeakReference<>(r));
if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size());
return r;
}
}
過(guò)程是:先從緩存中取,如果緩存中有且沒(méi)有過(guò)時(shí)夸溶,則直接返回逸吵,否則依次創(chuàng)建AssetManager 和Resources
AssetManager構(gòu)造函數(shù)
/**
* 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);
// 確保有能夠訪問(wèn)系統(tǒng)資源的AssetManager對(duì)象
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<jlong>(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),這里這個(gè)值是為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<mAssetPaths.size(); i++) {
if (mAssetPaths[i].path == ap.path) {
if (cookie) {
*cookie = static_cast<int32_t>(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<AssetManager*>(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<int32_t>(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構(gòu)造函數(shù)的第一步:init(false)缝裁,其主要工作是加載系統(tǒng)資源(framework-res.apk),以供后續(xù)應(yīng)用程序使用扫皱。而其第二部:ensureSystemAssets()也是為了創(chuàng)建系統(tǒng)資源使用對(duì)象AssetManager
private static void ensureSystemAssets() {
synchronized (sSync) {
if (sSystem == null) {
AssetManager system = new AssetManager(true);
system.makeStringBlocks(null);
sSystem = system;
}
}
}
初始化了AssetManager之后,就可以通過(guò)AssetManager.addAssetPath()加載本身資源了.
在AssetManager加載完相關(guān)資源后捷绑,就可以創(chuàng)建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.
* 設(shè)備分辨率相關(guān)信息:屏幕分辨率韩脑,density,font scaling
* @param config Desired device configuration to consider when
* selecting/computing resource values (optional).
* 該配置信息用來(lái)決定使用哪套資源
* @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;
}
// 設(shè)備相關(guān)配置信息更新處理
updateConfiguration(config, metrics);
// 創(chuàng)建字符串資源池
assets.ensureStringBlocks();
}