Android 资源加载Resources源码分析(8.0)

上下观古今,起伏千万途。这篇文章主要讲述Android 资源加载Resources源码分析(8.0)相关的知识,希望能为你提供帮助。
我们熟悉的资源加载代码:

1.Activity.getResources();

2.Context.getResources();

这2种方式获取的都是Resources对象
【Android 资源加载Resources源码分析(8.0)】先看第一种获取Resources对象源码分析:
说明:(AppcompatActivity中getResource()方法与Activity.getResources()是有区别的。AppcompatActivity是new Resources(...)对象)
 
一:Activity.getResources()源码分析:
Activity.getResources()之间调用的是Activity父类ContextThreadWrapper类中的
@Override public Resources getResources() { return getResourcesInternal(); }private Resources getResourcesInternal() { if (mResources == null) { if (mOverrideConfiguration == null) { mResources = super.getResources(); //继续向上 调用mBase.getReources()方法。其中mBase是一个Context对象 } else { final Context resContext = createConfigurationContext(mOverrideConfiguration); mResources = resContext.getResources(); } } return mResources; }

从上面知道Activity.getResources() 其实也是调用的Context.getResources()方法
 
二:继续分析Context.getResources()方法。
Context是一个抽象类。其中getResources()方法源码:
public abstract Resources getResources()

通过查询资料知道:ContextImpl是Context的实现类。(这里我没有知道源码实现的地方)
ContextImpl源码地址:http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/android/app/ContextImpl.java
@Override public Resources getResources() { return mResources; }

ContextImpl的getResources()方法中已实例化了Resources对象
 
mResources是通过setResources方法赋值:
void setResources(Resources r) { if (r instanceof CompatResources) { ((CompatResources) r).setContext(this); } mResources = r; }

查找到给mResources赋值的代码:
Android 资源加载Resources源码分析(8.0)

文章图片
Android 资源加载Resources源码分析(8.0)

文章图片
1static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { 2if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); 3ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, 4null); 5context.setResources(packageInfo.getResources()); 6return context; 7} 8 9static ContextImpl createActivityContext(ActivityThread mainThread, 10LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId, 11Configuration overrideConfiguration) { 12if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); 13 14String[] splitDirs = packageInfo.getSplitResDirs(); 15ClassLoader classLoader = packageInfo.getClassLoader(); 16 17if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) { 18Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies"); 19try { 20classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName); 21splitDirs = packageInfo.getSplitPaths(activityInfo.splitName); 22} catch (NameNotFoundException e) { 23// Nothing above us can handle a NameNotFoundException, better crash. 24throw new RuntimeException(e); 25} finally { 26Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); 27} 28} 29 30ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName, 31activityToken, null, 0, classLoader); 32 33// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY. 34displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY; 35 36final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY) 37? packageInfo.getCompatibilityInfo() 38: CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; 39 40final ResourcesManager resourcesManager = ResourcesManager.getInstance(); 41 42// Create the base resources for which all configuration contexts for this Activity 43// will be rebased upon. 44context.setResources(resourcesManager.createBaseActivityResources(activityToken, 45packageInfo.getResDir(), 46splitDirs, 47packageInfo.getOverlayDirs(), 48packageInfo.getApplicationInfo().sharedLibraryFiles, 49displayId, 50overrideConfiguration, 51compatInfo, 52classLoader)); 53context.mDisplay = resourcesManager.getAdjustedDisplay(displayId, 54context.getResources()); 55return context; 56}

mResources赋值代码分别调用的方法:
一: context.setResources(packageInfo.getResources());

二: context.setResources(resourcesManager.createBaseActivityResources(activityToken, packageInfo.getResDir(), splitDirs, packageInfo.getOverlayDirs(), packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfiguration, compatInfo, classLoader));

这里只分析第一种:(2种差不多。只是进入ResourcesManager方式不一样)
  继续查看LoadedApk类的getResources()方法
public Resources getResources() { if (mResources == null) { final String[] splitPaths; try { splitPaths = getSplitPaths(null); } catch (NameNotFoundException e) { // This should never fail. throw new AssertionError("null split not found"); }mResources = ResourcesManager.getInstance().getResources(null, mResDir, splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(), getClassLoader()); } return mResources; }

继续查看ResourcesManager的getResources()方法
public @Nullable Resources getResources(@Nullable IBinder activityToken, @Nullable String resDir, @Nullable String[] splitResDirs, @Nullable String[] overlayDirs, @Nullable String[] libDirs, int displayId, @Nullable Configuration overrideConfig, @NonNull CompatibilityInfo compatInfo, @Nullable ClassLoader classLoader) { try { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources"); //C的方法 final ResourcesKey key = new ResourcesKey(//计算哈希值 resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy compatInfo); classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader(); return getOrCreateResources(activityToken, key, classLoader); } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } }

继续查看 getOrCreateResources()方法
Android 资源加载Resources源码分析(8.0)

文章图片
Android 资源加载Resources源码分析(8.0)

文章图片
1private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken, 2@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) { 3synchronized (this) { 4if (DEBUG) { 5Throwable here = new Throwable(); 6here.fillInStackTrace(); 7Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here); 8} 9 10if (activityToken != null) { 11final ActivityResources activityResources = 12getOrCreateActivityResourcesStructLocked(activityToken); 13 14// Clean up any dead references so they don‘t pile up. 15ArrayUtils.unstableRemoveIf(activityResources.activityResources, 16sEmptyReferencePredicate); 17 18// Rebase the key‘s override config on top of the Activity‘s base override. 19if (key.hasOverrideConfiguration() 20& & !activityResources.overrideConfig.equals(Configuration.EMPTY)) { 21final Configuration temp = new Configuration(activityResources.overrideConfig); 22temp.updateFrom(key.mOverrideConfiguration); 23key.mOverrideConfiguration.setTo(temp); 24} 25 26 27 28ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key); 29if (resourcesImpl != null) { 30if (DEBUG) { 31Slog.d(TAG, "- using existing impl=" + resourcesImpl); 32} 33return getOrCreateResourcesForActivityLocked(activityToken, classLoader, 34resourcesImpl, key.mCompatInfo); 35} 36 37// We will create the ResourcesImpl object outside of holding this lock. 38 39} else { 40// Clean up any dead references so they don‘t pile up. 41ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate); 42 43// Not tied to an Activity, find a shared Resources that has the right ResourcesImpl 44ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key); 45if (resourcesImpl != null) { 46if (DEBUG) { 47Slog.d(TAG, "- using existing impl=" + resourcesImpl); 48} 49return getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo); 50} 51 52// We will create the ResourcesImpl object outside of holding this lock. 53} 54} 55 56// If we‘re here, we didn‘t find a suitable ResourcesImpl to use, so create one now. 57ResourcesImpl resourcesImpl = createResourcesImpl(key); 58if (resourcesImpl == null) { 59return null; 60} 61 62synchronized (this) { 63ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key); 64if (existingResourcesImpl != null) { 65if (DEBUG) { 66Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl 67+ " new impl=" + resourcesImpl); 68} 69resourcesImpl.getAssets().close(); 70resourcesImpl = existingResourcesImpl; 71} else { 72// Add this ResourcesImpl to the cache. 73mResourceImpls.put(key, new WeakReference< > (resourcesImpl)); 74} 75 76final Resources resources; 77if (activityToken != null) { 78resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader, 79resourcesImpl, key.mCompatInfo); 80} else { 81resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo); 82} 83return resources; 84} 85}

getOrCreateResources()方法其中比较重要方法:
ResourcesImpl resourcesImpl = createResourcesImpl(key);

resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);

 
先看:createResourcesImpl(key)方法,它去创建了一个ResourcesImpl对象
private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) { final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration); daj.setCompatibilityInfo(key.mCompatInfo); final AssetManager assets = createAssetManager(key); if (assets == null) { return null; }final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj); final Configuration config = generateConfig(key, dm); final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj); if (DEBUG) { Slog.d(TAG, "- creating impl=" + impl + " with key: " + key); } return impl; }

继续向下查看:getOrCreateResourcesLocked(..)方法
private @NonNull Resources getOrCreateResourcesLocked(@NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl, @NonNull CompatibilityInfo compatInfo) { // Find an existing Resources that has this ResourcesImpl set. final int refCount = mResourceReferences.size(); for (int i = 0; i < refCount; i++) { WeakReference< Resources> weakResourceRef = mResourceReferences.get(i); Resources resources = weakResourceRef.get(); if (resources != null & & Objects.equals(resources.getClassLoader(), classLoader) & & resources.getImpl() == impl) { if (DEBUG) { Slog.d(TAG, "- using existing ref=" + resources); } return resources; } }// Create a new Resources reference and use the existing ResourcesImpl object. Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader) : new Resources(classLoader); resources.setImpl(impl); mResourceReferences.add(new WeakReference< > (resources)); if (DEBUG) { Slog.d(TAG, "- creating new ref=" + resources); Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl); } return resources; }

到这里可以之间看到 Resources 对象是之间通过new Resources(ClassLoader)创建的
然后通过resources.setImpl(impl)去设置参数。
我们进一步去查看Resources.getString(int id)方法
@NonNull public String getString(@StringRes int id) throws NotFoundException { return getText(id).toString(); }

public CharSequence getText(@StringRes int id, CharSequence def) { CharSequence res = id != 0 ? mResourcesImpl.getAssets().getResourceText(id) : null; return res != null ? res : def; }

这里发现加载资源是通过ResourcesImpl对象去加载的
public AssetManager getAssets() { return mAssets; }

进一步查看AssetManager.getResourceText(int id)方法
@Nullable final CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = https://www.songbingjia.com/android/mValue; if (getResourceValue(resId, 0, outValue, true)) { return outValue.coerceToString(); } return null; } }

final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, boolean resolveRefs) { synchronized (this) { final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); if (block < 0) { return false; }// Convert the changing configurations flags populated by native code. outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { outValue.string = mStringBlocks[block].get(outValue.data); } return true; } }

继续往下 基本就是底层的一些东西了。
总结:Android 资源加载 核心就是AssetManager对象。
(后面写的不是特别详细。。。)

    推荐阅读