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