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

Posted 不一样的农民

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 资源加载Resources源码分析(8.0)相关的知识,希望对你有一定的参考价值。

我们熟悉的资源加载代码:

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     }
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()方法

技术分享图片
 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     }
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 = 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对象。

(后面写的不是特别详细。。。)

以上是关于Android 资源加载Resources源码分析(8.0)的主要内容,如果未能解决你的问题,请参考以下文章

android 皮肤包换肤之Resources加载

springboot-静态资源访问

springboot-静态资源访问

Android 插件化Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )

Android 插件化“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )

Android 安装包优化资源混淆 ( resources.arsc 资源映射表混淆 | resources.arsc 资源映射表二进制格式分析 | 混淆全局字符串池和资源名称字符串池 )