:总结与实战建议)
系列目录第一篇全景图与调用链路概览 | 第二篇Locale 与 LocaleList | 第三篇Configuration | 第四篇LocalePicker | 第五篇ActivityManagerService | 第六篇ActivityThread | 第七篇Resources 与 AssetManager | 第八篇总结与实战建议一、完整调用链回顾八篇文章逐层递进完整还原了 Android 7 国际化的端到端链路。下面将其浓缩为一次用户切换语言的完整时序图用户点击EnglishSettings App 主线程 │ ▼ ┌─────────────────────────────────┐ │ 第四篇LocalePicker │ │ │ │ config.setLocales(en-US) │ │ config.userSetLocale true │ │ am.updatePersistentConfig(config)│ └─────────────┬───────────────────┘ │ [Binder IPC] ▼ ┌─────────────────────────────────┐ │ 第五篇AMS │ │ │ │ updatePersistentConfiguration() │ ← Binder 入口 │ ├─ enforceCallingPermission() │ ← 权限检查 │ ├─ clearCallingIdentity() │ ← 身份切换 │ └─ updateConfigurationLocked()│ │ ├─ updateFrom(values) │ ← 差异计算 → CONFIG_LOCALE │ ├─ persist.sys.locale │ ← 持久化到系统属性 │ ├─ applyToSystemServer() │ ← 自身先更新 │ └─ for each ProcessRecord: │ scheduleConfigChanged() ──[Binder]──→ └─────────────────────────────────┘ │ ▼ (进入每个应用进程) ┌─────────────────────────────────┐ │ 第六篇ActivityThread │ │ │ │ ApplicationThread │ ← Binder 线程收到 │ └─ H.CONFIGURATION_CHANGED │ ← 切换到主线程 │ └─ handleConfigurationChanged() │ ├─ applyConfigToResourcesLocked() │ └─ collectComponentCallbacks() │ └─ for each Activity/App/Service: │ onConfigurationChanged() └─────────────┬───────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 第七篇Resources AssetMgr │ │ │ │ ResourcesManager │ │ └─ 遍历所有缓存的 ResourcesImpl│ │ └─ ResourcesImpl │ │ ├─ calcConfigChanges │ ← 计算差异并更新快照 │ ├─ 多语言最佳匹配 │ │ ├─ cache.onConfigChange()│ ← 选择性清除缓存 │ └─ AssetManager.setConfiguration(en-US) │ └─ [JNI] AssetManager.cpp │ └─ ResTable::setParameters() │ │ │ 下次 getString(id) ────┘ │ ↓ │ ResTable 用新 locale 重新匹配 │ ↓ │ 返回 Hello而非你好 └─────────────────────────────────┘总耗时粗略估算阶段操作耗时Settings → AMSBinder IPC同步~0.5msAMS 内部配置更新 持久化~1-2msAMS → 所有进程Binder IPC × N 个进程~1-5ms取决于进程数应用进程内部Resources 重建 回调~5-20ms取决于资源量下一次 getStringNative 资源匹配~0.01ms内存操作二、各层核心源码文件速查表层次文件路径数据模型Locale.javalibcore/luni/src/main/java/java/util/Locale.java数据模型LocaleList.javaframeworks/base/core/java/android/os/LocaleList.java配置容器Configuration.javaframeworks/base/core/java/android/content/res/Configuration.java用户入口LocalePicker.javaframeworks/base/core/java/com/android/internal/app/LocalePicker.java用户入口LocaleStore.javaframeworks/base/core/java/com/android/internal/app/LocaleStore.java用户入口LocaleDragAndDropAdapter.javapackages/apps/Settings/src/.../LocaleDragAndDropAdapter.java系统服务ActivityManagerService.javaframeworks/base/services/core/java/com/android/server/am/ActivityManagerService.java应用主线程ActivityThread.javaframeworks/base/core/java/android/app/ActivityThread.java资源 APIResources.javaframeworks/base/core/java/android/content/res/Resources.java资源实现ResourcesImpl.javaframeworks/base/core/java/android/content/res/ResourcesImpl.java资源管理ResourcesManager.javaframeworks/base/core/java/android/app/ResourcesManager.javaJNI 桥AssetManager.javaframeworks/base/core/java/android/content/res/AssetManager.javaNative 引擎AssetManager.cppframeworks/base/libs/androidfw/AssetManager.cpp资源类型ResourceTypes.h/.cppframeworks/base/libs/androidfw/ResourceTypes.cpp三、关键设计思想总结3.1 单向数据流Android 7 的国际化采用严格的自上而下的数据流用户操作 → Settings App → AMS → 各应用进程 → Resources → Native 资源匹配关键设计不存在应用自行修改全局语言的路径。普通应用无法调用 AMS 的语言变更接口受CHANGE_CONFIGURATION权限保护这保证了系统的稳定性和一致性。3.2 缓存 重创建模式ResourcesManager不修改已有的Resources对象而是创建新的旧的Resources被WeakReference持有 → 无引用后 GC 回收新的Resources以新配置创建 → 内部AssetManager使用新 locale所有引用旧Resources的组件通过onConfigurationChanged()获取新引用关键设计这种immutable recreate模式避免了并发修改问题也天然支持了配置切换场景。3.3 分层解耦整个国际化系统清晰地分层层职责对外接口数据层Locale / LocaleList 的表示与序列化BCP 47 标签、Parcelable传输层Configuration 的打包与 Binder 传输Parcelable分发层AMS 的权限检查、存储、逐进程通知IActivityManager响应层ActivityThread 的资源重建与组件回调ComponentCallbacks2匹配层Native 层的资源表索引与匹配AssetManager JNI每一层只关心自己的职责层间通过标准接口Parcelable、Binder、JNI通信。3.4 兼容性优先Android 7 在引入LocaleList的同时通过以下手段保持了向后兼容Configuration.getLocales()/setLocales()→ 新 APILocalePicker.updateLocale()→ 包装为updateLocales(new LocaleList(locale))旧版Configuration.locale→Deprecated但保留公有字段通过fixUpLocaleList()同步mLocaleList四、应用层实战建议基于对 AOSP 7 国际化机制的深入理解给出以下实践建议4.1 声明 configChangesactivityandroid:name.MainActivityandroid:configChangeslocale|layoutDirection|screenSize|orientation/声明configChangeslocale可避免语言切换时 Activity 重启。但需要自己在onConfigurationChanged()中刷新 UI。注意如果只声明了locale而没有声明layoutDirection从 RTL 语言切换到 LTR 语言时 Activity 仍可能因为布局方向变化而重启。4.2 资源文件组织推荐的资源目录结构res/ ├── values/ ← 默认语言通常是英语 │ └── strings.xml ├── values-zh-rCN/ ← 简体中文 │ └── strings.xml ├── values-zh-rTW/ ← 繁体中文 │ └── strings.xml ├── values-ja/ ← 日语 │ └── strings.xml └── values-ko/ ← 韩语 └── strings.xml注意事项values/必须存在作为兜底默认值使用 Android 标准的语言标识符ISO 639-1 ISO 3166-1图片资源也可以用drawable-zh-rCN/做本地化4.3 运行时语言切换// 获取系统语言LocalecurrentLocalegetResources().getConfiguration().getLocales().get(0);// 手动设置 Activity 语言不改变全局ConfigurationconfiggetResources().getConfiguration();config.setLocale(newLocale);getResources().updateConfiguration(config,getResources().getDisplayMetrics());// 注意以上操作只影响当前 Activity退出后不持久4.4 onConfigurationChanged 最佳实践OverridepublicvoidonConfigurationChanged(ConfigurationnewConfig){super.onConfigurationChanged(newConfig);// 1. 更新语言相关 UIupdateLocaleDependentUI();// 2. 重建 ActionBar如果语言改变了文字方向if(mActionBar!null){mActionBar.onConfigurationChanged(newConfig);}// 3. 刷新布局如果布局因语言有差异recreateIfNeeded();}4.5 避免的陷阱陷阱原因建议用静态变量缓存语言语言切换后不会自动更新每次从 Resources 读取硬编码字符串无法被翻译全部放入 strings.xml忽略多语言列表只取locale[0]可能丢失用户偏好支持 LocaleList 的全部优先级在 Service 中忽略回调Service 也会收到 onConfigurationChanged同样处理语言切换Fragment 中重复处理Activity 会自动分发到 Fragment不要在 Fragment 中再做一遍五、Android 版本差异速览各版本在国际化方面的关键变化APIAndroid 版本国际化关键变化21-235.0 - 6.0单一 LocaleConfiguration.locale字段24-257.0 - 7.1LocaleList 引入updatePersistentConfiguration()拖拽排序26-278.0 - 8.1ResourcesImpl分离加强ResourcesManager优化289.0Per-app language每应用独立语言本系列基于 AOSP 7.x 分析后续版本的演变以此为基。六、结语Android 7 的国际化系统是 Android 框架设计哲学的典范数据流清晰自顶向下的单向传播分层解耦数据 → 配置 → 分发 → 响应 → 匹配各司其职兼容性在引入 LocaleList 的同时平滑兼容旧 API性能优化缓存池、异步持久化、渐进式 UI 更新理解这套机制不仅有助于解决国际化相关的 Bug如切换语言后部分文字未变化、“Fragment 中语言未更新”更能深入领会 Android 系统架构的设计思想——这套 Binder Handler 资源管理 Native 引擎的四层架构在其他系统机制屏幕旋转、暗黑模式切换、字体大小变更中也同样适用。希望这个系列能帮助你从会用国际化进阶到理解国际化。本系列源码基于 AOSP 7.xAPI 24/25各版本可能存在差异请以实际代码为准。