我们熟悉的资源加载代码:
1.Activity.getResources();
2.Context.getResources();
这2种方式获取的都是Resources对象
先看第一种获取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赋值的代码:
1 static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { 2 if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); 3 ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, 4 null); 5 context.setResources(packageInfo.getResources()); 6 return context; 7 } 8 9 static ContextImpl createActivityContext(ActivityThread mainThread, 10 LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId, 11 Configuration overrideConfiguration) { 12 if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); 13 14 String[] splitDirs = packageInfo.getSplitResDirs(); 15 ClassLoader classLoader = packageInfo.getClassLoader(); 16 17 if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) { 18 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies"); 19 try { 20 classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName); 21 splitDirs = packageInfo.getSplitPaths(activityInfo.splitName); 22 } catch (NameNotFoundException e) { 23 // Nothing above us can handle a NameNotFoundException, better crash. 24 throw new RuntimeException(e); 25 } finally { 26 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); 27 } 28 } 29 30 ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName, 31 activityToken, null, 0, classLoader); 32 33 // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY. 34 displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY; 35 36 final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY) 37 ? packageInfo.getCompatibilityInfo() 38 : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; 39 40 final ResourcesManager resourcesManager = ResourcesManager.getInstance(); 41 42 // Create the base resources for which all configuration contexts for this Activity 43 // will be rebased upon. 44 context.setResources(resourcesManager.createBaseActivityResources(activityToken, 45 packageInfo.getResDir(), 46 splitDirs, 47 packageInfo.getOverlayDirs(), 48 packageInfo.getApplicationInfo().sharedLibraryFiles, 49 displayId, 50 overrideConfiguration, 51 compatInfo, 52 classLoader)); 53 context.mDisplay = resourcesManager.getAdjustedDisplay(displayId, 54 context.getResources()); 55 return context; 56 }
分别调用的方法:
一: 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()方法
1 private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken, 2 @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) { 3 synchronized (this) { 4 if (DEBUG) { 5 Throwable here = new Throwable(); 6 here.fillInStackTrace(); 7 Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here); 8 } 9 10 if (activityToken != null) { 11 final ActivityResources activityResources = 12 getOrCreateActivityResourcesStructLocked(activityToken); 13 14 // Clean up any dead references so they don‘t pile up. 15 ArrayUtils.unstableRemoveIf(activityResources.activityResources, 16 sEmptyReferencePredicate); 17 18 // Rebase the key‘s override config on top of the Activity‘s base override. 19 if (key.hasOverrideConfiguration() 20 && !activityResources.overrideConfig.equals(Configuration.EMPTY)) { 21 final Configuration temp = new Configuration(activityResources.overrideConfig); 22 temp.updateFrom(key.mOverrideConfiguration); 23 key.mOverrideConfiguration.setTo(temp); 24 } 25 26 27 28 ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key); 29 if (resourcesImpl != null) { 30 if (DEBUG) { 31 Slog.d(TAG, "- using existing impl=" + resourcesImpl); 32 } 33 return getOrCreateResourcesForActivityLocked(activityToken, classLoader, 34 resourcesImpl, 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. 41 ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate); 42 43 // Not tied to an Activity, find a shared Resources that has the right ResourcesImpl 44 ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key); 45 if (resourcesImpl != null) { 46 if (DEBUG) { 47 Slog.d(TAG, "- using existing impl=" + resourcesImpl); 48 } 49 return 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. 57 ResourcesImpl resourcesImpl = createResourcesImpl(key); 58 if (resourcesImpl == null) { 59 return null; 60 } 61 62 synchronized (this) { 63 ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key); 64 if (existingResourcesImpl != null) { 65 if (DEBUG) { 66 Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl 67 + " new impl=" + resourcesImpl); 68 } 69 resourcesImpl.getAssets().close(); 70 resourcesImpl = existingResourcesImpl; 71 } else { 72 // Add this ResourcesImpl to the cache. 73 mResourceImpls.put(key, new WeakReference<>(resourcesImpl)); 74 } 75 76 final Resources resources; 77 if (activityToken != null) { 78 resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader, 79 resourcesImpl, key.mCompatInfo); 80 } else { 81 resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo); 82 } 83 return resources; 84 } 85 }
其中比较重要方法:
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 = 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对象。
(后面写的不是特别详细。。。)