源码解析:可能是全网最细的Android-资源加载机制剖析,墙裂建议收藏

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 = obtainTempTypedValue();
try {
final ResourcesImpl impl = mResourcesImpl;
impl.getValue(id, value, true);
return impl.loadDrawable(this, value, id, theme, true);
} finally {
releaseTempTypedValue(value);
}
}

和对于字符串资源的装载类似,首先根据资源ID获取一个TypedValue对象,然后利用TypedValue实例,通过AssetManager进行装载。

void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
if (found) {
return;
}
}

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;
}

Drawable资源的获取核心代码是在对于ResourcesImplloadDrawable函数的调用。

@Nullable
Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme,
boolean useCache) throws NotFoundException {
try {
if (TRACE_FOR_PRELOAD) {
if ((id >>> 24) == 0x1) {
final String name = getResourceName(id);
if (name != null) {
Log.d(“PreloadDrawable”, name);
}
}
}
//判断是否为ColorDrawable
final boolean isColorDrawable;
final DrawableCache caches;
final long key;
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
&& value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
isColorDrawable = true;
caches = mColorDrawableCache;
key = value.data;
} else {
isColorDrawable = false;
caches = mDrawableCache;
key = (((long) value.assetCookie) << 32) | value.data;
}

//,是否存在查找的Drawable
if (!mPreloading && useCache) {
final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
if (cachedDrawable != null) {
return cachedDrawable;
}
}

// 检查预加载的资源文件中,是否存在要查找的Drawable
final Drawable.ConstantState cs;
if (isColorDrawable) {
cs = sPreloadedColorDrawables.get(key);
} else {
cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
}

//创建Drawable
Drawable dr;
if (cs != null) {
dr = cs.newDrawable(wrapper);
} else if (isColorDrawable) {
dr = new ColorDrawable(value.data);
} else {
dr = loadDrawableForCookie(wrapper, value, id, null);
}

// 对Drawable的主题进行处理
final boolean canApplyTheme = dr != null && dr.canApplyTheme();
if (canApplyTheme && theme != null) {
dr = dr.mutate();
dr.applyTheme(theme);
dr.clearMutated();
}

// 将装载的Drawable资源加入到缓存之中
if (dr != null && useCache) {
dr.setChangingConfigurations(value.changingConfigurations);
cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
}
return dr;
} catch (Exception e) {

}
}

loadDrawableForCookie

根据TypedValue中存储的信息,从XML文件或者资源流中构建Drawable

private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id,
Resources.Theme theme) {
if (value.string == null) {
throw new NotFoundException(“Resource “” + getResourceName(id) + “” (”

  • Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
    }
    //解析值的文件名
    final String file = value.string.toString();

if (TRACE_FOR_MISS_PRELOAD) {
// Log only framework resources
if ((id >>> 24) == 0x1) {
final String name = getResourceName(id);
if (name != null) {
Log.d(TAG, “Loading framework drawable #” + Integer.toHexString(id)

  • ": " + name + " at " + file);
    }
    }
    }

final Drawable dr;
//如果文件后缀为xml,通过XmlResourceParser构建Drawable对象
try {
if (file.endsWith(“.xml”)) {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, “drawable”);
dr = Drawable.createFromXml(wrapper, rp, theme);
rp.close();
} else {
//从文件流中构建Drawable对象
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
is.close();
}
} catch (Exception e) {

}
return dr;
}

XmlResourceParser loadXmlResourceParser(@NonNull String file, @AnyRes int id, int assetCookie,
@NonNull String type)
throws NotFoundException {
if (id != 0) {
try {
synchronized (mCachedXmlBlocks) {
final int[] cachedXmlBlockCookies = mCachedXmlBlockCookies;
final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
// 检测缓存是否在我们需要的资源
final int num = cachedXmlBlockFiles.length;
for (int i = 0; i < num; i++) {
if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
&& cachedXmlBlockFiles[i].equals(file)) {
return cachedXmlBlocks[i].newParser();
}
}

// 如果资源不在缓存之中,这通过AssetManager去装载,然后加入到缓存中
final XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
if (block != null) {
final int pos = (mLastCachedXmlBlockIndex + 1) % num;
mLastCachedXmlBlockIndex = pos;
final XmlBlock oldBlock = cachedXmlBlocks[pos];
if (oldBlock != null) {
oldBlock.close();
}
cachedXmlBlockCookies[pos] = assetCookie;
cachedXmlBlockFiles[pos] = file;
cachedXmlBlocks[pos] = block;
return block.newParser();
}
}
} catch (Exception e) {

}
}
}

图片资源的装载流程是首先将根据ID获得TypedValue实例,然后根据TypedValue进行查找Drawable资源,首先检测缓存中是否有该资源,如果没有从预加载资源中查找,如果预加载资源中也没有,判断要加载的资源类型,如果为colorDrawable,这根据Typedvalue进行创建,否则通过加载xml或者文件输入流进行处理来获得Drawable对象。

资源的装载分为两步,一个是通过资源ID得到ID对应的TypedValue对象,对于简单的资源,通过TypedValue即可,对于复杂的资源,需要第二步来把资源文件装载到内存之中。

AssetManager

在创建Resources的构造函数,创建ResourcesImpl的时候调用了AssetManager的getSystem方法,该方法用来确保创建唯一的AssetManager实例。

public static AssetManager getSystem() {
ensureSystemAssets();
return sSystem;
}

保证全局只有一个AssetManager

private static void ensureSystemAssets() {
synchronized (sSync) {
if (sSystem == null) {
AssetManager system = new AssetManager(true);
system.makeStringBlocks(null);
sSystem = system;
}
}
}

private AssetManager(boolean isSystem) {
if (DEBUG_REFS) {
synchronized (this) {
mNumRefs = 0;
incRefsLocked(this.hashCode());
}
}
init(true);
}

static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
if (isSystem) {
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));
}

bool AssetManager::addDefaultAssets()
{
const char* root = getenv(“ANDROID_ROOT”);
LOG_ALWAYS_FATAL_IF(root == NULL, “ANDROID_ROOT not set”);

String8 path(root);
path.appendPath(kSystemAssets);

return addAssetPath(path, NULL, false /* appAsLib /, true / isSystemAsset */);
}

设置资源路径,通过对这个值的修改可以实现动态加载。

public final int addAssetPath(String path) {
return addAssetPathInternal(path, false);
}

private final int addAssetPathInternal(String path, boolean appAsLib) {
synchronized (this) {
int res = addAssetPathNative(path, appAsLib);
makeStringBlocks(mStringBlocks);
return res;
}
}

添加资源路径

bool AssetManager::addAssetPath(
const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset)
{
AutoMutex _l(mLock);

asset_path ap;

String8 realPath(path);
if (kAppZipName) {
realPath.appendPath(kAppZipName);
}
ap.type = ::getFileType(realPath.string());
if (ap.type == kFileTypeRegular) {
ap.path = realPath;
} else {
ap.path = path;
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());

ap.isSystemAsset = isSystemAsset;
mAssetPaths.add(ap);

// new paths are always added at the end
if (cookie) {
*cookie = static_cast<int32_t>(mAssetPaths.size());
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。

image

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。

[外链图片转存中…(img-CfThctl4-1713681896308)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 29
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 源码解析是指对 Android 操作系统源代码进行深入分析和研究,以便更好地了解 Android 操作系统的内部工作原理和实现机制Android 源码解析可以帮助开发者更好地掌握 Android 平台开发技能,了解 Android 系统的架构和设计思想,同时也可以为定制和优化 Android 系统提供支持。 Android 源码解析包括以下方面: 1. Android 系统架构和设计思想:包括 Android 系统的各个组件、框架、运行时环境等的设计思想和实现原理。 2. Android 系统启动流程:包括 Android 系统启动的各个阶段、各个组件的启动顺序及其作用等。 3. Android 系统的核心组件:包括 Activity、Service、BroadcastReceiver、ContentProvider 等的实现原理和使用方法。 4. Android 框架层:包括 Android 框架层的各个组件和类库的实现原理和使用方法。 5. Android 运行时环境:包括 Dalvik 虚拟机、ART 虚拟机、Zygote 进程等的实现原理和使用方法。 6. Android 系统的内存管理和性能优化:包括 Android 系统内存管理的原理和优化方法,以及 Android 系统的性能优化方法。 7. Android 系统的定制和优化:包括对 Android 系统进行定制和优化的方法和技巧,以及如何移植 Android 系统到不同的硬件平台上。 总之,Android 源码解析是学习 Android 开发不可或缺的重要环节,通过深入分析 Android 系统的源代码,开发者可以更好地理解 Android 系统的内部工作原理,提高 Android 开发的技能和水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值