美文网首页
Android 浅析 Resources 和 AssetMana

Android 浅析 Resources 和 AssetMana

作者: 一半晴天 | 来源:发表于2018-02-11 18:19 被阅读296次

在 Android 开发中我们使用 Resources 来获取 res 目录下的各种与设备相关的资源。而使用 AssetManager 来获取 assets 目录下的资源。

一般来说 Resources 对象通过 Context 获得。
appContext 中的 resources 是在创建之后通过如下代码设置。
context.setResources(packageInfo.getResources());
LoadedApk 中则是通过如下代码创建:

            mResources = ResourcesManager.getInstance().getResources(null, mResDir,
                    splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
                    Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
                    getClassLoader());

其中 mResDir 对应 ApplicationInfo.sourceDir 字段

    /**
     * Full path to the base APK for this application.
     */
    public String sourceDir;

ResourcesManager.getInstance().getResources 的主要逻辑如下:

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

getOrCreateResources 主要逻辑如下:

 // 1) 先创建 ResourcesImpl
 ResourcesImpl resourcesImpl = createResourcesImpl(key);
// 2) 再创建 Resources
 resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);

createResourcewImpl 主要逻辑如下:

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

        return impl;

其中 createAssetManager(key) 的主要逻辑如下:

// 1) 创建 assets 对象实例。
  AssetManager assets = new AssetManager();
// 2) 添加各资源目录。
assets.addAssetPath(key.mResDir)
assets.addAssetPath(splitResDir) 
assets.addOverlayPath(idmapPath);
assets.addAssetPathAsSharedLibrary(libDir) 

最终 Resources 的创建逻辑如下:

// getOrCreateResourcesLocked
        Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
                : new Resources(classLoader);
        resources.setImpl(impl);

以 Resouces.getString 来查看资源的读取流程

1)调用 getText

 public String getString(@StringRes int id) throws NotFoundException {
        return getText(id).toString();
    }
  1. 通过 resourcesImpl 调用 AssetsManagergetResourceText
    @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));
    }
  1. getResourceText
    @Nullable
    final CharSequence getResourceText(@StringRes int resId) {
        synchronized (this) {
            final TypedValue outValue = mValue;
            if (getResourceValue(resId, 0, outValue, true)) {
                return outValue.coerceToString();
            }
            return null;
        }
    }
  1. getResourceValue
    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;
        }
    }

最后通过 loadResourceValue 这一原生方法读取。

   /** Returns true if the resource was found, filling in mRetStringBlock and
     *  mRetData. */
    private native final int loadResourceValue(int ident, short density, TypedValue outValue,
            boolean resolve);

一点小总结

资源的加载的主要底层逻辑还是由 AssetsManager 来处理的。
一些动态主题包的实现原理主要是通过。AssetsManager.addAssetPath(String path) 这一接口来创建对应主题包的 Resources 对象来实现的。

相关文章

网友评论

      本文标题:Android 浅析 Resources 和 AssetMana

      本文链接:https://www.haomeiwen.com/subject/xwqltftx.html