Android對國際化與多語言切換已經做得不錯了,一個應用只要命名相應語系的values-[language]文件夾,通過“設置”→“語言&鍵盤”→“選擇語言”即可實現(xiàn)應用多種語言的切換。
但如何在應用里自己實現(xiàn)?搜索過發(fā)現(xiàn)網上有如下的做法:
- Resources res = getResources();
- Configuration config = res.getConfiguration();
- config.locale = locale;
- DisplayMetrics dm = res.getDisplayMetrics();
- res.updateConfiguration(config, dm);
親測,不成功。好吧,程序員又到了自力更生的時候了。下面開始講應用多語言切換的三種方法。
先上效果圖:

前兩種方法的原理即在應用里實現(xiàn)“選擇語言”。通過查看源碼,其核心代碼為:
- IActivityManager iActMag = ActivityManagerNative.getDefault();
- try {
- Configuration config = iActMag.getConfiguration();
- config.locale = locale;
-
-
- iActMag.updateConfiguration(config);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- PS:感謝 曾陽 的幫助。
可以發(fā)現(xiàn)IActivityManager與ActivityManagerNative都是非公開類。如何調用?第一種是API欺騙,第二種是使用Java反射機制。
1. API欺騙
燒制到手機中的android.jar包含了Android所需的各種類與方法;而供開發(fā)者使用的android.jar只是其中的一部分。API欺騙是指在應用中去模擬未公開的類和方法讓應用編譯通過并生成APK,然而在應用實際運行中調用的卻仍是燒制到手機中真實的android.jar。
通過核心代碼可以看到我們要模擬的是ActivityManagerNative中的一個方法getDefault()和IActivityManager中的兩個方法getConfiguration()與updateConfiguration(config)。參照源碼,應用的工程結構圖及代碼模擬如下:
工程結構圖:

代碼:
- ActivityManagerNative.java
- package android.app;
-
-
-
-
-
- public abstract class ActivityManagerNative {
- public static IActivityManager getDefault() {
- return null;
- }
- }
-
- IActivityManager.java
- package android.app;
-
- import android.content.res.Configuration;
- import android.os.RemoteException;
-
-
-
-
-
- public abstract interface IActivityManager {
- public abstract Configuration getConfiguration() throws RemoteException;
-
- public abstract void updateConfiguration(Configuration paramConfiguration)
- throws RemoteException;
- }
實現(xiàn)模擬了這兩個類后,即可正常使用上面提到的轉換語系的核心代碼了。
2. Java反射機制
不多說了,Java反射機制入門教程:
http://java./developer/technicalArticles/ALT/Reflection/index.html
之前寫過的幾個使用Java反射的例子:
[Android]獲取未安裝的APK圖標(原創(chuàng)非轉帖)
http://blog.csdn.net/sodino/article/details/6215224
[Android]掛斷、接聽電話
http://blog.csdn.net/sodino/article/details/6181610
直接上代碼:
- private void updateLanguage(Locale locale) {
- Log.d("ANDROID_LAB", locale.toString());
- try {
- Object objIActMag, objActMagNative;
- Class clzIActMag = Class.forName("android.app.IActivityManager");
- Class clzActMagNative = Class.forName("android.app.ActivityManagerNative");
- Method mtdActMagNative$getDefault = clzActMagNative.getDeclaredMethod("getDefault");
-
- objIActMag = mtdActMagNative$getDefault.invoke(clzActMagNative);
-
- Method mtdIActMag$getConfiguration = clzIActMag.getDeclaredMethod("getConfiguration");
- Configuration config = (Configuration) mtdIActMag$getConfiguration.invoke(objIActMag);
- config.locale = locale;
-
-
-
- Class[] clzParams = { Configuration.class };
- Method mtdIActMag$updateConfiguration = clzIActMag.getDeclaredMethod(
- "updateConfiguration", clzParams);
- mtdIActMag$updateConfiguration.invoke(objIActMag, config);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
實際運行后,發(fā)現(xiàn)對當前系統(tǒng)設置了新的Locale后,不單自己的應用語系改變了,系統(tǒng)所有的應用語系都改變了。這肯定是不合理的。有一個解決辦法是在應用界面退出前再次對系統(tǒng)設置成碑的Locale,不過個人不喜歡這樣的辦法,加之調用updateConfiguration()方法后,整個Activity會重新onCreate(),這個考慮Activity的生命周期可有點費勁了。于是有了下面這第三種方法。
3. 自己轉換語系(哈哈,這個名字很現(xiàn)實啊)
動手實現(xiàn)嘛,啥都系統(tǒng)弄好了,那程序員的存在還有什么意義呢。
自己轉換語系有點麻煩,先看工程結構圖:
values/strings.xml與xml/english.xml的內容是相同的;values-zh-rCN/strings.xml與xml/chinese.xml的內容也是相同的。出現(xiàn)這樣的冗余是因為生成APK時values下的內容都打到rasc去了,讀取不了了。
自己實現(xiàn)語系的轉換需要考慮到:
3.1 R.xxxxx.id與對應語系中文本串的對應(需要特別考慮到R.array.string字符串數組)。
3.2 解析xml。
3.3 設置語系后,所有界面元素的手動刷新。
在xml中聲明一個string是這個的格式:
- <string name="app_name">語言應用</string>
對應R文件會生成一個id指代該string
- public static final class string {
- public static final int app_name=0x7f050001;
- }
3.1的問題就是如何實現(xiàn)id與string的匹配,解決方法為:
- Resources res = context.getResources();
- String pkg = context.getPackageName();
- String tag = "app_name";
- int idTag = res.getIdentifier(tag, "string", pkg);
3.2 解析XML
這兒要用到一個新的工具了:XmlResourceParser,解析過程有點繞,但比SAX簡單些。具體細節(jié)見LanguageApp_Sodino工程中的代碼吧。
3.3 手動刷新界面。
要獲取所有涉及到語系更新組件的索引逐一更新,體力活兒,細心點花點力氣也可實現(xiàn)。
詳細實現(xiàn)過程見下面三個工程中:
LanguageApp_APICheat LanguageApp_Reflection LanguageApp_Sodino (PS:不要問我為什么下載的工程在IDE中為什么無法直接使用,為什么打開是亂碼紅叉一大堆,既然是程序員,遇到問題是不是也該自己多思考思考呢。)