前言
上一篇文章,讲到了Android中进程的启动和一个Activity的创建到显示流程,现在本篇要分析的是在Android中资源的装载机制,例如字符串资源,图片资源是如何被装载的。这里将从字符串和图片两种类型资源展开分析,同时对于后面所利用的资源装载的内容也会做简单的分析。
Resources源码剖析
对于资源的装载机制,这里核心的几个类是Resources,ResourcesImpl,AssetManager。Resources算是对于ResourcesImpl的一个代理,Resources的所有调用都是会调用到ResourcesImpl上,在ResourcesImpl的内部,具备对于资源的Cache和AssetManager,对于资源的装载会首先从其Cache中进行查找,当查找不到的时候,会调用AssetManager进行相应资源的装载,装载之后会在ResourcesImpl中将资源缓存下来。
Resource 中有一个内部静态变量
static Resources mSystem = null;
在getSystem方法中进行了初始化,作为对于内部变量的持有被保存着,其初次的调用是在zygote创建新进程的时候,预加载资源的时候被调用。
public static Resources getSystem() {
synchronized (sSync) {
Resources ret = mSystem;
if (ret == null) {
ret = new Resources();
mSystem = ret;
}
return ret;
}
}
Resrouce对象的创建,在Resrouce中的各种操作,最终真正的执行者是ResourcesImpl。
private Resources() {
this(null);
final DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
final Configuration config = new Configuration();
config.setToDefaults();
mResourcesImpl = new ResourcesImpl(AssetManager.getSystem(), metrics, config,
new DisplayAdjustments());
}
在Resources的构造函数中创建ResourcesImpl的实例。
public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
mAssets = assets;
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
mAssets.ensureStringBlocks();
}
在创建ResoucesImpl实例的时候,获得了AssetManager的实例,其负责了应用层和资源文件的交互。Resource对象的获得,是通过ContextImpl方法中获得,获得方式是返回了其内部的变量mResource变量,
resources = mResourcesManager.getResources(
activityToken,
packageInfo.getResDir(),
packageInfo.getSplitResDirs(),
packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
packageInfo.getClassLoader());
调用了ResourcesManager的getOrCreateResources方法。其实现为从activityResources中查找,如果查找不到,则会重新创建一个,然后加入到activityResources中,并返回。
获取字符串资源
从一个获取资源文件的方法看起,这里从一个获取文字的方法入手。
@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));
}
public AssetManager getAssets() {
return mAssets;
}
调用AssetManager的getResourceText
final CharSequence getResourceText(@StringRes int resId) {
synchronized (this) {
final TypedValue outValue = mValue;
if (getResourceValue(resId, 0, outValue, true)) {
return outValue.coerceToString();
}
return null;
}
}
首先根据id获得TypedValue,然后根据TypedValue获得我们需要的资源。
final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
if (block < 0) {
return false;
}
if (outValue.type == TypedValue.TYPE_STRING) {
outValue.string = mStringBlocks[block].get(outValue.data);
}
return true;
}
对于字符串资源,其值就存在TypedValue中,所以在获得了TypedValue之后,就可以通过其来获得资源值。
获取图片资源
由于图片资源的特殊性,相比于字符串资源的获取,要复杂一些,这里从上层的获取方法开始进行分析。
public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
throws NotFoundException {
final TypedValue value = obtainTempTypedValu