通过反射调用 Android 系统内部 API 来全局修改设备的语言设置。这是一种 非官方、高风险、需要系统权限(甚至 Root) 的做法,通常只在定制 ROM、系统级应用或测试环境中使用。
📌实际调用:
java
Cmd.rootCommand("pm grant " + getAppPackageName() + " android.permission.CHANGE_CONFIGURATION");
setLanguage(locale);
📌rootCommand方法:
java
public static boolean rootCommand(String command) {
Process process = null;
DataOutputStream os = null;
try {
process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
os.writeBytes(command + "\n");
os.writeBytes("exit\n");
os.flush();
process.waitFor();
} catch (Exception e) {
return false;
} finally {
try {
if (os != null) {
os.close();
}
process.destroy();
} catch (Exception e) {
}
}
return true;
}
📌setLanguage方法:
java
public void setLanguage(Locale locale) {
try {
Class clzIActMag = Class.forName("android.app.IActivityManager");
Class clzActMagNative = Class.forName("android.app.ActivityManagerNative");
Method mtdActMagNative$getDefault = clzActMagNative.getDeclaredMethod("getDefault");
Object objIActMag = mtdActMagNative$getDefault.invoke(clzActMagNative);
Method mtdIActMag$getConfiguration = clzIActMag.getDeclaredMethod("getConfiguration");
Configuration config = (Configuration) mtdIActMag$getConfiguration.invoke(objIActMag);
config.setLocale(locale);
Class clzConfig = Class.forName("android.content.res.Configuration");
Field userSetLocale = clzConfig.getField("userSetLocale");
userSetLocale.set(config, true);
Class[] clzParams = { Configuration.class };
Method mtdIActMag$updateConfiguration = clzIActMag.getDeclaredMethod("updateConfiguration", clzParams);
mtdIActMag$updateConfiguration.invoke(objIActMag, config);
BackupManager.dataChanged("com.android.providers.settings");
// String s = "setprop persist.sys.language "+locale.getLanguage()+"\n"+
// "setprop persist.sys.country "+locale.getCountry()+"\n"+
// "am broadcast -a android.intent.action.LOCALE_CHANGED \n";
// new Thread(() -> Cmd.rootCommand(s)).start();
} catch (Exception e) {
e.printStackTrace();
}
}
🔍 代码作用详解
目标:
强制将整个 Android 系统的语言切换为指定的 Locale(如 zh-CN、en-US 等),而不仅仅是当前 App。
⚠️ 注意:这不是普通 App 应该做的事情!普通应用只能修改自己的语言(通过
Configuration+ContextWrapper),不能也不应修改系统全局语言。
代码流程解析:
java
Class clzIActMag = Class.forName("android.app.IActivityManager");
Class clzActMagNative = Class.forName("android.app.ActivityManagerNative");
- 尝试加载 Android 内部类
IActivityManager和ActivityManagerNative(这些是隐藏的系统 API,普通 SDK 不暴露)。
java
Method mtdActMagNative$getDefault = clzActMagNative.getDeclaredMethod("getDefault");
Object objIActMag = mtdActMagNative$getDefault.invoke(clzActMagNative);
- 调用
ActivityManagerNative.getDefault()获取IActivityManager的 Binder 代理对象(用于与 system_server 通信)。
java
Method mtdIActMag$getConfiguration = clzIActMag.getDeclaredMethod("getConfiguration");
Configuration config = (Configuration) mtdIActMag$getConfiguration.invoke(objIActMag);
- 从系统获取当前全局的
Configuration(包含语言、字体、方向等)。
java
config.setLocale(locale);
- 修改
Configuration中的语言为传入的locale。
java
Field userSetLocale = clzConfig.getField("userSetLocale");
userSetLocale.set(config, true);
- 设置
userSetLocale = true,表示这是用户主动设置的语言(影响系统行为,如是否同步到 SettingsProvider)。
java
Method mtdIActMag$updateConfiguration = clzIActMag.getDeclaredMethod("updateConfiguration", Configuration.class);
mtdIActMag$updateConfiguration.invoke(objIActMag, config);
- 调用
IActivityManager.updateConfiguration(config),通知系统更新全局配置。 - 这会触发所有正在运行的应用重新加载资源(相当于系统语言切换)。
java
BackupManager.dataChanged("com.android.providers.settings");
- 通知备份服务:设置数据已变更(用于云同步,如 Google 备份)。
被注释掉的部分(Root 命令):
java
// String s = "setprop persist.sys.language "+locale.getLanguage()+"\n"+
// "setprop persist.sys.country "+locale.getCountry()+"\n"+
// "am broadcast -a android.intent.action.LOCALE_CHANGED \n";
// new Thread(() -> Cmd.rootCommand(s)).start();
- 如果设备已 Root,可通过 shell 命令直接修改系统属性并广播
LOCALE_CHANGED。 - 这是另一种更底层的强制改语言方式,但 必须 Root。
⚠️ 风险与问题
| 问题 | 说明 |
|---|---|
| 需要系统签名或 Root | 普通第三方 App 无法调用 IActivityManager.updateConfiguration(),会抛出 SecurityException。 |
| Android 版本兼容性差 | ActivityManagerNative 在 Android 8.0(API 26)后被移除,改用 ActivityManager.getService()。此代码在新版本会崩溃。 |
| 破坏用户体验 | 强制修改系统语言会影响所有 App,用户可能困惑。 |
| 违反 Google Play 政策 | 此类行为会被视为恶意操作,应用会被下架。 |
| 不稳定 | 反射调用隐藏 API,系统更新后极易失效。 |
✅ 正确做法(针对普通 App)
如果你只是想 让自己的 App 切换语言,请使用标准方式:
Kotlin 示例(推荐):
kotlin
val locale = Locale("zh")
Locale.setDefault(locale)
val config = Configuration(resources.configuration).apply {
setLocale(locale)
}
resources.updateConfiguration(config, resources.displayMetrics)
// 然后 recreate() Activity
或使用 AndroidX 的新 API(targetSdk ≥ 33):
kotlin
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags("zh-Hans"))
强烈建议普通开发者不要使用此类方法,应采用 Android 官方支持的本地化方案。