opoojkk

Android 字符串资源加载流程解析

lxx
目次

Android 字符串资源解析全流程解析 #

说明:本文基于 Android API 36.0 和 Android Code Search(2025.12.7 最新 android-latest-release 分支)进行源码分析。

在 Android 应用中,我们常写下这样一行代码:

1
val appName = context.getString(R.string.app_name)

看起来只是取一个字符串,但这行调用实际上穿过了从 Java 层到 Native 层的完整资源解析流程。下面以 string 为例,看系统是如何一步步把资源 ID 转成最终的字符串。

从 Context 到 AssetManager #

入口是 Context

1
2
3
4
// frameworks/base/core/java/android/content/Context.java
public String getString(int id) {
    return getResources().getString(id);
}

ContextImpl 内部持有 ResourcesImpl,它最终把请求交给 AssetManager

1
2
3
4
5
6
7
8
// frameworks/base/core/java/android/content/res/Resources.java
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));
}

getResourceText() 会继续下沉到:

1
2
// frameworks/base/core/java/android/content/res/AssetManager.java
getResourceValue(resId, densityDpi, outValue, resolveRefs)

进入 JNI 前执行:

1
2
// frameworks/base/core/java/android/content/res/AssetManager.java
ensureValidLocked();

JNI 层:统一的资源访问入口 #

Android 将所有标量资源(string、color、integer、dimension 等)统一通过 NativeGetResourceValue() 访问:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// frameworks/base/core/jni/android_util_AssetManager.cpp
static jint NativeGetResourceValue(...){
  auto assetmanager = LockAndStartAssetManager(ptr);

  auto value = assetmanager->GetResource(resid, false /*may_be_bag*/, density);

  if (!value.has_value()) {
    return ApkAssetsCookieToJavaCookie(kInvalidCookie);
  }

  if (resolve_references) {
    auto result = assetmanager->ResolveReference(value.value());
    if (!result.has_value()) {
      return ApkAssetsCookieToJavaCookie(kInvalidCookie);
    }
  }

  return CopyValue(env, *value, typed_value);
}

GetResource:查找资源、识别类型 #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// frameworks/base/libs/androidfw/AssetManager2.cpp
auto result = FindEntry(resid, density_override, false, false);
if (!result.has_value()) {
  return base::unexpected(result.error());
}

// 如果 entry 是 map(style、array 等),则返回引用类型
if (auto map_entry = std::get_if<ResTable_map_entry>(&result->entry)) {
  if (!may_be_bag) return base::unexpected(std::nullopt);
  return SelectedValue(TYPE_REFERENCE, resid, result->cookie,
                       result->type_flags, resid, result->config);
}

// 简单值:string、int、color
Res_value value = std::get<Res_value>(result->entry);

// 处理动态资源 ID
result->dynamic_ref_table->lookupResourceValue(&value);

return SelectedValue(value.dataType, value.data, result->cookie,
                     result->type_flags, resid, result->config);

FindEntry:拆解 ID 并遍历所有设备配置 #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// frameworks/base/libs/androidfw/ResourceUtils.h
inline uint8_t get_package_id(uint32_t resid) {
  return static_cast<uint8_t>((resid >> 24) & 0x000000ffu);
}

inline uint8_t get_type_id(uint32_t resid) {
  return static_cast<uint8_t>((resid >> 16) & 0x000000ffu);
}

inline uint16_t get_entry_id(uint32_t resid) {
  return static_cast<uint16_t>(resid & 0x0000ffffu);
}

遍历所有配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// frameworks/base/libs/androidfw/AssetManager2.cpp
for (auto& config : configurations_) {
    const ResTable_config* desired = &config;

    if (density_override != 0 && density_override != config.density) {
        density_override_config = config;
        density_override_config.density = density_override;
        desired = &density_override_config;
    }

    auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired, ...);
    ...
}

FindEntryInternal:匹配策略 #

1
2
3
4
5
6
// frameworks/base/libs/androidfw/AssetManager2.cpp
if (entry_config.match(requested_config)) {
    if (entry_config.isBetterThan(best_config, requested_config)) {
        best = entry;
    }
}

Res_value:完整结构(string = 字符串池索引) #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h
struct Res_value
{
    uint16_t size;
    uint8_t res0;

    enum : uint8_t {
        TYPE_NULL = 0x00,
        TYPE_REFERENCE = 0x01,
        TYPE_ATTRIBUTE = 0x02,
        TYPE_STRING = 0x03,
        TYPE_FLOAT = 0x04,
        TYPE_DIMENSION = 0x05,
        TYPE_FRACTION = 0x06,
        TYPE_DYNAMIC_REFERENCE = 0x07,
        TYPE_DYNAMIC_ATTRIBUTE = 0x08,

        TYPE_FIRST_INT = 0x10,
        TYPE_INT_DEC = 0x10,
        TYPE_INT_HEX = 0x11,
        TYPE_INT_BOOLEAN = 0x12,

        TYPE_FIRST_COLOR_INT = 0x1c,
        TYPE_INT_COLOR_ARGB8 = 0x1c,
        TYPE_INT_COLOR_RGB8 = 0x1d,
        TYPE_INT_COLOR_ARGB4 = 0x1e,
        TYPE_INT_COLOR_RGB4 = 0x1f,
        TYPE_LAST_COLOR_INT = 0x1f,
        TYPE_LAST_INT = 0x1f
    };
    uint8_t dataType;

    enum {
        COMPLEX_UNIT_SHIFT = 0,
        COMPLEX_UNIT_MASK = 0xf,
        COMPLEX_UNIT_PX = 0,
        COMPLEX_UNIT_DIP = 1,
        COMPLEX_UNIT_SP = 2,
        COMPLEX_UNIT_PT = 3,
        COMPLEX_UNIT_IN = 4,
        COMPLEX_UNIT_MM = 5,

        COMPLEX_UNIT_FRACTION = 0,
        COMPLEX_UNIT_FRACTION_PARENT = 1,

        COMPLEX_RADIX_SHIFT = 4,
        COMPLEX_RADIX_MASK = 0x3,
        COMPLEX_RADIX_23p0 = 0,
        COMPLEX_RADIX_16p7 = 1,
        COMPLEX_RADIX_8p15 = 2,
        COMPLEX_RADIX_0p23 = 3,

        COMPLEX_MANTISSA_SHIFT = 8,
        COMPLEX_MANTISSA_MASK = 0xffffff
    };

    enum {
        DATA_NULL_UNDEFINED = 0,
        DATA_NULL_EMPTY = 1
    };

    uint32_t data;

    void copyFrom_dtoh(const Res_value& src) {
      if constexpr (kDeviceEndiannessSame) {
        *this = src;
      } else {
        copyFrom_dtoh_slow(src);
      }
    }
};

异常处理 #

1
2
// frameworks/base/core/java/android/content/res/Resources.java
throw new NotFoundException("String resource ID #0x" + Integer.toHexString(id));

加载流程图(示意) #

标签:
Categories: