Java中使用反射的地方較多,尤其是各種框架中。最近在Android7.0的項目中遇到個問題很奇怪,反射使用的類找不到了,但是編譯的時候沒問題啊。然后在代碼中使用非反射的方式調用代碼也是沒有問題的,這時奇怪的現象出現了,加入手動調用代碼后反射代碼找不到類的問題也不出現了。其實這個是混淆代碼所做的工作,一個類沒有被使用的情況下會在編譯中直接刪除掉,顯然并沒有考慮反射調用的情況。關閉混淆或者修改混淆的配置文件即可解決這個問題。各種框架自動所做的工作大部分是節省了程序員的時間,但是一旦出問題查起來花費的時間也是不少
當處于應用層時,如果只是修改應用內多語言時,上層app可以輕松完成各種語言的切換,網上方法很多,就不在詳細敘述,app內部設置多語言可參考下面這篇文章
Android 實現應用內置語言切換(附有源碼下載地址7.0可用)。
但是,如何通過app,設置系統語言呢?這正是本文討論核心。
android6.0
android設置系統語言的核心方法在framework層,地址是\frameworks\base\core\java\com\android\internal\app\LocalePicker.java類里,方法如下:
? ?/**
? ? ?* Requests the system to update the system locale. Note that the system looks halted
? ? ?* for a while during the Locale migration, so the caller need to take care of it.
? ? ?*/
? ? public static void updateLocale(Locale locale) {
? ? ? ? try {
? ? ? ? ? ? IActivityManager am = ActivityManagerNative.getDefault();
? ? ? ? ? ? Configuration config = am.getConfiguration();
? ? ? ? ? ? config.setLocale(locale);
? ? ? ? ? ? config.userSetLocale = true;
? ? ? ? ? ? am.updateConfiguration(config);
? ? ? ? ? ? // Trigger the dirty bit for the Settings Provider.
? ? ? ? ? ? BackupManager.dataChanged("com.android.providers.settings");
? ? ? ? } catch (RemoteException e) {
? ? ? ? ? ? // Intentionally left blank
? ? ? ? }
? ? }
android6.0設置系統語言的關鍵邏輯就是上面那個方法。如果上層APP想要設置系統語言必須通過反射方法獲取,核心方法代碼如下(本方法可以持久化系統語言設置,也就是說重啟手機后不會恢復默認系統語言):
private void changeSystemLanguage(Locale locale) {if (locale != null) {try {Class classActivityManagerNative = Class.forName("android.app.ActivityManagerNative"); Method getDefault = classActivityManagerNative.getDeclaredMethod("getDefault"); Object objIActivityManager = getDefault.invoke(classActivityManagerNative); Class classIActivityManager = Class.forName("android.app.IActivityManager"); Method getConfiguration = classIActivityManager.getDeclaredMethod("getConfiguration"); Configuration config = (Configuration) getConfiguration.invoke(objIActivityManager); config.setLocale(locale); //config.userSetLocale = true; Class clzConfig = Class.forName("android.content.res.Configuration"); java.lang.reflect.Field userSetLocale = clzConfig.getField("userSetLocale"); userSetLocale.set(config, true); Class[] clzParams = {Configuration.class}; Method updateConfiguration = classIActivityManager.getDeclaredMethod("updateConfiguration", clzParams); updateConfiguration.invoke(objIActivityManager, config); BackupManager.dataChanged("com.android.providers.settings"); } catch (Exception e) {Log.d(TAG, "changeSystemLanguage: " + e.getLocalizedMessage()); }} }調用時如下:
Local locale = Locale.ENGLISH;
系統語言就變成了英文。(不再贅述)changeSystemLanguage(locale);
android7.0
7.0與6.0的源碼有所不同,LocalePicker.Java定義系統語言的方式,不在是一種Local,而是一個LocaleList,具體方法如下:
? public static void updateLocale(Locale locale) {
? ? ? ? updateLocales(new LocaleList(locale));
? ? }
updateLocale調用了updateLocales方法,updateLocales方法如下:
?public static void updateLocales(LocaleList locales) {
? ? ? ? try {
? ? ? ? ? ? final IActivityManager am = ActivityManagerNative.getDefault();
? ? ? ? ? ? final Configuration config = am.getConfiguration();
? ? ? ? ? ? config.setLocales(locales);
? ? ? ? ? ? config.userSetLocale = true;
? ? ? ? ? ? am.updatePersistentConfiguration(config);
? ? ? ? ? ? // Trigger the dirty bit for the Settings Provider.
? ? ? ? ? ? BackupManager.dataChanged("com.android.providers.settings");
? ? ? ? } catch (RemoteException e) {
? ? ? ? ? ? // Intentionally left blank
? ? ? ? }
? ? }
可見,6.0上的反射直接照搬到7.0是不起作用的,需要重新運用反射方法,反射方法代碼如下
protected void changeSystemLanguage(LocaleList locale) {if (locale != null) {try {Class classActivityManagerNative = Class.forName("android.app.ActivityManagerNative"); Method getDefault = classActivityManagerNative.getDeclaredMethod("getDefault"); Object objIActivityManager = getDefault.invoke(classActivityManagerNative); Class classIActivityManager = Class.forName("android.app.IActivityManager"); Method getConfiguration = classIActivityManager.getDeclaredMethod("getConfiguration"); Configuration config = (Configuration) getConfiguration.invoke(objIActivityManager); config.setLocales(locale); Class[] clzParams = {Configuration.class}; Method updateConfiguration = classIActivityManager.getDeclaredMethod("updatePersistentConfiguration", clzParams); updateConfiguration.invoke(objIActivityManager, config); } catch (Exception e) {Log.d(TAG, "changeSystemLanguage: " + e.getLocalizedMessage()); }} }
6.0和7.0設置系統語言源碼不同之出,有兩點:
1.設置參數的方法不同,6.0是updateConfiguration,7.0是updatePersistentConfiguration,這點需要注意
2.6.0傳遞的local,而7.0是一個列表LocaleList
7.0反射方法調用如下:
Locale newLocale = new Locale("zh", "CN"); final LocaleList localeList = new LocaleList(newLocale); changeSystemLanguage(localeList);
android7.0app切換系統語言Demo源碼下載:http://download.csdn.net/download/zhaokai621/9930068
生成的apk,需要系統簽名,可放在源碼vendor\customer\你的文件下通過mm編譯,需要注意的是,需要有.mk文件,生成的apk在out的目錄下(當然層級有很多),把out目錄下生成的apkpush到手機system/priv-app/你的文件夾,重啟手機即可。
操作如下:
1.把studio生成的apk放入如下目錄:
Android.mk文件如下(供參考):
--------------------------開始(下面才是)-------------------------------------------------------------
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Module name should match apk name to be installed
LOCAL_MODULE := Test
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_OVERRIDES_PACKAGES := Calendar
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := platform
#LOCAL_PREBUILT_JNI_LIBS:= \
#@lib/armeabi/liblocSDK4d.so
#LOCAL_MODULE_PATH := $(TARGET_OUT)/vendor/operator/app
LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PREBUILT)
--------------------------------結束(本行不是)-----------------------------------------------------------------------------
2.編譯源碼,單編模塊
命令如下(前提是你已經整編過一套android源碼)
source ./build/envsetup.sh(加載命令)
lunch 16(序號和你整編時選的一樣,本文以android7.0源碼為準)
mmm?vendor/customer/Test
3.生成的apk(已經打包了系統簽名):在如下目錄(可能有出入)
4.push到手機,不要install,重啟手機
adb push XXXX system/priv-app/Test
XXX是你out生成的apk,可拖拽到此。
現在就可以看到一個應用了,點擊就可以切換系統語言了。多多交流 ? *-*
android7.0app切換系統語言Demo源碼下載:http://download.csdn.net/download/zhaokai621/9930068