如何动态更新或替换Android应用对应语言的strings.xml文件
Android 动态更新strings.xml实现方案
原生Android的strings.xml是编译时打包进APK的,没法直接替换原文件,所以得换个思路——绕过系统原生资源加载,自定义一套字符串读取逻辑,优先用服务器下载的语言资源, fallback到系统默认资源。下面是具体实现步骤:
一、定义服务器返回的资源格式
建议用JSON格式(比XML解析更简单高效),比如对应中文的资源:
{ "app_name": "我的新应用", "welcome_msg": "欢迎使用动态更新版", "btn_submit": "提交" }
如果坚持用XML,也可以让服务器返回标准的<resources>结构的XML文件。
二、下载并存储资源
- 发起网络请求下载对应语言的资源文件(根据当前系统Locale判断要下载的语言包,比如
zh-CN、en-US) - 把下载的文件存在应用内部存储的私有目录(比如
context.getFilesDir()),避免外部存储权限问题,同时可以按语言+版本号命名,比如strings_zh-CN_v2.json,方便版本管理和更新判断。
示例代码(用OkHttp做下载):
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://your-server.com/langs/strings_zh-CN.json") .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { // 下载失败,继续用默认资源 } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful() && response.body() != null) { String jsonContent = response.body().string(); // 保存到内部存储 File file = new File(context.getFilesDir(), "strings_zh-CN.json"); FileWriter writer = new FileWriter(file); writer.write(jsonContent); writer.close(); // 通知界面刷新字符串 LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("REFRESH_STRINGS")); } } });
三、实现自定义字符串加载工具类
写一个单例工具类,优先读取本地存储的资源,没有的话调用系统getString():
public class DynamicStringManager { private static DynamicStringManager instance; private Context context; private Map<String, String> dynamicStrings = new HashMap<>(); private String currentLang; private DynamicStringManager(Context context) { this.context = context.getApplicationContext(); loadCurrentLangStrings(); } public static synchronized DynamicStringManager getInstance(Context context) { if (instance == null) { instance = new DynamicStringManager(context); } return instance; } // 加载当前语言的动态字符串 private void loadCurrentLangStrings() { currentLang = Locale.getDefault().getLanguage() + "-" + Locale.getDefault().getCountry(); File file = new File(context.getFilesDir(), "strings_" + currentLang + ".json"); if (file.exists()) { try { String jsonContent = new String(Files.readAllBytes(file.toPath())); JSONObject jsonObject = new JSONObject(jsonContent); Iterator<String> keys = jsonObject.keys(); while (keys.hasNext()) { String key = keys.next(); dynamicStrings.put(key, jsonObject.getString(key)); } } catch (Exception e) { e.printStackTrace(); } } } // 获取字符串:优先动态资源,否则用系统默认 public String getString(String key) { if (dynamicStrings.containsKey(key)) { return dynamicStrings.get(key); } int resId = context.getResources().getIdentifier(key, "string", context.getPackageName()); return resId != 0 ? context.getString(resId) : key; } // 资源更新后重新加载 public void reloadStrings() { dynamicStrings.clear(); loadCurrentLangStrings(); } }
四、替换应用中的字符串调用
把所有getString(R.string.xxx)替换成DynamicStringManager.getInstance(this).getString("xxx"),比如:
// 原来的写法 textView.setText(getString(R.string.welcome_msg)); // 替换后的写法 textView.setText(DynamicStringManager.getInstance(this).getString("welcome_msg"));
五、更新后的界面刷新
当资源下载完成后,通过广播或者ViewModel通知所有界面重新加载字符串,比如在Activity中注册广播:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LocalBroadcastManager.getInstance(this).registerReceiver(refreshReceiver, new IntentFilter("REFRESH_STRINGS")); } private BroadcastReceiver refreshReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { DynamicStringManager.getInstance(context).reloadStrings(); // 刷新当前界面的所有文本 refreshUI(); } }; private void refreshUI() { TextView welcomeTv = findViewById(R.id.welcome_tv); welcomeTv.setText(DynamicStringManager.getInstance(this).getString("welcome_msg")); // 其他控件同理 } @Override protected void onDestroy() { super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(refreshReceiver); }
注意事项
- 要处理语言切换的情况:当用户切换系统语言时,需要重新加载对应语言的动态资源
- 加入版本校验:给每个语言包加版本号,下载前先比对服务器版本和本地版本,避免重复下载
- 异常处理:解析资源文件失败时,确保能 fallback到系统默认字符串,避免崩溃
内容的提问来源于stack exchange,提问作者Ayush Sharma
相关产品推荐
相关产品推荐

