在activity中获取资源的方式是getResource()方法
activity的getresource会进入父类ContexThemeWrapper中
@Override
public Resources getResources() {
return getResourcesInternal();
}
private Resources getResourcesInternal() {
if (mResources == null) {
if (mOverrideConfiguration == null) {
mResources = super.getResources();
} else {
final Context resContext = createConfigurationContext(mOverrideConfiguration);
第一步获取context
mResources = resContext.getResources();
第二步resource是context中获取的
}
}
return mResources;
}
接下来
@Override
public Context createConfigurationContext(Configuration overrideConfiguration) {
return mBase.createConfigurationContext(overrideConfiguration);
}
回到mBase中,我们知道mBase是contextImpl对象
public Context createConfigurationContext(Configuration overrideConfiguration) {
if (overrideConfiguration == null) {
throw new IllegalArgumentException("overrideConfiguration must not be null");
}
if (mForceDisplayOverrideInResources) {
// Ensure the resources display metrics are adjusted to match the display this context
// is based on.
Configuration displayAdjustedConfig = new Configuration();
displayAdjustedConfig.setTo(mDisplay.getDisplayAdjustments().getConfiguration(),
ActivityInfo.CONFIG_WINDOW_CONFIGURATION, 1);
displayAdjustedConfig.updateFrom(overrideConfiguration);
overrideConfiguration = displayAdjustedConfig;
}
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
mAttributionSource.getAttributionTag(),
mAttributionSource.getNext(),
mSplitName, mToken, mUser, mFlags, mClassLoader, null);
context.mIsConfigurationBasedContext = true;
final int displayId = getDisplayId();
final Integer overrideDisplayId = mForceDisplayOverrideInResources
? displayId : null;
context.setResources(createResources(mToken, mPackageInfo, mSplitName, overrideDisplayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
mResources.getLoaders()));
此处设置activity的的resource。
setresource的对象是createResources方法创建的,接下来看看。
return context;
}
private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
@Nullable Integer overrideDisplayId, Configuration overrideConfig,
CompatibilityInfo compatInfo, List<ResourcesLoader> resourcesLoader) {
final String[] splitResDirs;
final ClassLoader classLoader;
try {
splitResDirs = pi.getSplitPaths(splitName);
classLoader = pi.getSplitClassLoader(splitName);
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
return ResourcesManager.getInstance().getResources(activityToken,
pi.getResDir(),
splitResDirs,
pi.getOverlayDirs(),
pi.getOverlayPaths(),
pi.getApplicationInfo().sharedLibraryFiles,
overrideDisplayId,
overrideConfig,
compatInfo,
classLoader,
resourcesLoader);
}
通过ResourcesManager创建ResourceManger对象是单例的。在ActivityThread创建的时候执行第一次创建
ActivityThread() {
mResourcesManager = ResourcesManager.getInstance();
}
由此我们知道当应用第一次创建的时候创建activityThread并创建resourceManger就加载了此应用的资源。。
接下来看下resourceManger.getResouce()
@Nullable
public Resources getResources(
@Nullable IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] legacyOverlayDirs,
@Nullable String[] overlayPaths,
@Nullable String[] libDirs,
@Nullable Integer overrideDisplayId,
@Nullable Configuration overrideConfig,
@NonNull CompatibilityInfo compatInfo,
@Nullable ClassLoader classLoader,
@Nullable List<ResourcesLoader> loaders) {
....
Resources resources;
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY,
overrideConfig,
compatInfo,
loaders == null ? null : loaders.toArray(new ResourcesLoader[0]));
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
resourceKey是有资源目录,显示屏,横竖屏,等等创建的
if (activityToken != null) {
Configuration initialOverrideConfig = new Configuration(key.mOverrideConfiguration);
rebaseKeyForActivity(activityToken, key, overrideDisplayId != null);
resources = createResourcesForActivity(activityToken, key, initialOverrideConfig,
overrideDisplayId, classLoader, assetsSupplier);
} else {
resources = createResources(key, classLoader, assetsSupplier);
}
return resources;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
以上通过createResouces方法创建
@Nullable
private Resources createResources(@NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
synchronized (mLock) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
Slog.w(TAG, "!! Create resources for key=" + key, here);
}
ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
if (resourcesImpl == null) {
return null;
}
return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
}
}
private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
@NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
此处查找或者生成resource
ResourcesImpl impl = findResourcesImplForKeyLocked(key);
if (impl == null) {
没有查找到是第一次用此资源
impl = createResourcesImpl(key, apkSupplier);
if (impl != null) {
存放到缓存里面
mResourceImpls.put(key, new WeakReference<>(impl));
}
}
return impl;
}
private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
final AssetManager assets = createAssetManager(key, apkSupplier);
if (assets == null) {
return null;
}
final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
daj.setCompatibilityInfo(key.mCompatInfo);
final Configuration config = generateConfig(key);
final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
此处创建了resourceImpl
if (DEBUG) {
Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
}
return impl;
}
接下来我们看获取应用的字符串方法
getResource().getText()方法,
@NonNull public CharSequence getText(@StringRes int id) throws NotFoundException {
CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
if (res != null) {
return res;
}
throw new NotFoundException("String resource ID #0x"
+ Integer.toHexString(id));
}
由此方法可知,获取字体的方法又assetManager调用。其实resourcesImpl所有的获取资源的方法都是由assetManager调用的
我们看下resourcesImpl的assetManager哪里来的
private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
final AssetManager assets = createAssetManager(key, apkSupplier);
此处得到assetManager
if (assets == null) {
return null;
}
final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
daj.setCompatibilityInfo(key.mCompatInfo);
final Configuration config = generateConfig(key);
final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
if (DEBUG) {
Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
}
return impl;
}
接下来
private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
final AssetManager.Builder builder = new AssetManager.Builder();
此处生成了用了builder模式生成了builder
final ArrayList<ApkKey> apkKeys = extractApkKeys(key);
遍历apkkey添加到builder中
for (int i = 0, n = apkKeys.size(); i < n; i++) {
final ApkKey apkKey = apkKeys.get(i);
try {
添加apk的资源
builder.addApkAssets(
(apkSupplier != null) ? apkSupplier.load(apkKey) : loadApkAssets(apkKey));
} catch (IOException e) {
if (apkKey.overlay) {
Log.w(TAG, String.format("failed to add overlay path '%s'", apkKey.path), e);
} else if (apkKey.sharedLib) {
Log.w(TAG, String.format(
"asset path '%s' does not exist or contains no resources",
apkKey.path), e);
} else {
Log.e(TAG, String.format("failed to add asset path '%s'", apkKey.path), e);
return null;
}
}
}
if (key.mLoaders != null) {
for (final ResourcesLoader loader : key.mLoaders) {
builder.addLoader(loader);
}
}
return builder.build();
}
接下来我们看下apk的资源都放在哪吧
private static @NonNull ArrayList<ApkKey> extractApkKeys(@NonNull final ResourcesKey key) {
final ArrayList<ApkKey> apkKeys = new ArrayList<>();
// 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 (key.mResDir != null) {
apkKeys.add(new ApkKey(key.mResDir, false /*sharedLib*/, false /*overlay*/));
}
第一项 res目录
if (key.mSplitResDirs != null) {
第二项 splitRes目录
for (final String splitResDir : key.mSplitResDirs) {
apkKeys.add(new ApkKey(splitResDir, false /*sharedLib*/, false /*overlay*/));
}
}
if (key.mLibDirs != null) {
第三项 lib的资源目录,比如so库,等等。
for (final String libDir : key.mLibDirs) {
// Avoid opening files we know do not have resources, like code-only .jar files.
if (libDir.endsWith(".apk")) {
apkKeys.add(new ApkKey(libDir, true /*sharedLib*/, false /*overlay*/));
}
}
}
if (key.mOverlayPaths != null) {
第四项 mOverlayPaths的资源目录 for (final String idmapPath : key.mOverlayPaths) {
apkKeys.add(new ApkKey(idmapPath, false /*sharedLib*/, true /*overlay*/));
}
}
return apkKeys;
}
上述方法把应用的资源包路径放在assetManger路径下,接下来看下assetmanager的build方法。
public AssetManager build() {
// Retrieving the system ApkAssets forces their creation as well.
final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
系统apk的资源
// Filter ApkAssets so that assets provided by multiple loaders are only included once
// in the AssetManager assets. The last appearance of the ApkAssets dictates its load
// order.
final ArrayList<ApkAssets> loaderApkAssets = new ArrayList<>();
final ArraySet<ApkAssets> uniqueLoaderApkAssets = new ArraySet<>();
for (int i = mLoaders.size() - 1; i >= 0; i--) {
final List<ApkAssets> currentLoaderApkAssets = mLoaders.get(i).getApkAssets();
for (int j = currentLoaderApkAssets.size() - 1; j >= 0; j--) {
final ApkAssets apkAssets = currentLoaderApkAssets.get(j);
if (uniqueLoaderApkAssets.add(apkAssets)) {
loaderApkAssets.add(0, apkAssets);
}
}
}
final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size()
+ loaderApkAssets.size();
final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount];
System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length);
// Append user ApkAssets after system ApkAssets.
for (int i = 0, n = mUserApkAssets.size(); i < n; i++) {
apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i);
}
// Append ApkAssets provided by loaders to the end.
for (int i = 0, n = loaderApkAssets.size(); i < n; i++) {
apkAssets[i + systemApkAssets.length + mUserApkAssets.size()] =
loaderApkAssets.get(i);
}
以上讲路径加入金apkseets
// Calling this constructor prevents creation of system ApkAssets, which we took care
// of in this Builder.
final AssetManager assetManager = new AssetManager(false /*sentinel*/);
assetManager.mApkAssets = apkAssets;
AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
false /*invalidateCaches*/);
assetManager.mLoaders = mLoaders.isEmpty() ? null
: mLoaders.toArray(new ResourcesLoader[0]);
return assetManager;
}
此处就创建了assetmanager。
gettext方法。由assetmanager
@UnsupportedAppUsage
@Nullable CharSequence getResourceText(@StringRes int resId) {
synchronized (this) {
final TypedValue outValue = mValue;
if (getResourceValue(resId, 0, outValue, true)) {
return outValue.coerceToString();
}
return null;
}
}
boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
Objects.requireNonNull(outValue, "outValue");
synchronized (this) {
ensureValidLocked();
final int cookie = nativeGetResourceValue(
mObject, resId, (short) densityDpi, outValue, resolveRefs);
if (cookie <= 0) {
return false;
}
// Convert the changing configurations flags populated by native code.
outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
if ((outValue.string = getPooledStringForCookie(cookie, outValue.data)) == null) {
return false;
}
}
return true;
}
}
此后native调用从 arsc文件中获取资源。
assetManager传入自定义包的地址可以做插件化获取apk中的资源。此处不多说了