android以欺骗的方法使用隐藏API调用举例(国际化,多语言)
2015-11-20 10:25
459 查看
Android对国际化与多语言切换已经做得不错了,一个应用只要命名相应语系的values-[language]文件夹,通过“设置”→“语言&键盘”→“选择语言”即可实现应用多种语言的切换。 但如何在应用里自己实现?搜索过发现网上有如下的做法: Resources res = getResources(); Configuration config = res.getConfiguration(); config.locale = locale; DisplayMetrics dm = res.getDisplayMetrics(); res.updateConfiguration(config, dm); 复制代码 亲测,不成功。好吧,程序员又到了自力更生的时候了。下面开始讲应用多语言切换的三种方法。 先上效果图: 前两种方法的原理即在应用里实现“选择语言”。通过查看源码,其核心代码为: IActivityManager iActMag = ActivityManagerNative.getDefault(); try { Configuration config = iActMag.getConfiguration(); config.locale = locale; // 此处需要声明权限:android.permission.CHANGE_CONFIGURATION // 会重新调用 onCreate(); iActMag.updateConfiguration(config); } catch (RemoteException e) { e.printStackTrace(); } PS:感谢 曾阳 的帮助。 复制代码 可以发现IActivityManager与ActivityManagerNative都是非公开类。如何调用?第一种是API欺骗,第二种是使用Java反射机制。 1. API欺骗 烧制到手机中的android.jar包含了Android所需的各种类与方法;而供开发者使用的android.jar只是其中的一部分。API欺骗是指在应用中去模拟未公开的类和方法让应用编译通过并生成APK,然而在应用实际运行中调用的却仍是烧制到手机中真实的android.jar。 通过核心代码可以看到我们要模拟的是ActivityManagerNative中的一个方法getDefault()和IActivityManager中的两个方法getConfiguration()与updateConfiguration(config)。参照源码,应用的工程结构图及代码模拟如下: 工程结构图: 代码: ActivityManagerNative.java package android.app; /** * @author Sodino E-mail:sodinoopen@hotmail.com * @version Time:2011-7-10 上午11:37:01 */ public abstract class ActivityManagerNative { public static IActivityManager getDefault() { return null; } } IActivityManager.java package android.app; import android.content.res.Configuration; import android.os.RemoteException; /** * @author Sodino E-mail:sodinoopen@hotmail.com * @version Time:2011-7-10 上午11:37:46 */ public abstract interface IActivityManager { public abstract Configuration getConfiguration() throws RemoteException; public abstract void updateConfiguration(Configuration paramConfiguration) throws RemoteException; } 复制代码 实现模拟了这两个类后,即可正常使用上面提到的转换语系的核心代码了。 2. Java反射机制 不多说了,Java反射机制入门教程: http://java.sun.com/developer/te ... flection/index.html 之前写过的几个使用Java反射的例子: [Android]获取未安装的APK图标(原创非转帖) /article/2637265.html [Android]挂断、接听电话 /article/2637263.html 直接上代码: 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"); // IActivityManager iActMag = ActivityManagerNative.getDefault(); objIActMag = mtdActMagNative$getDefault.invoke(clzActMagNative); // Configuration config = iActMag.getConfiguration(); Method mtdIActMag$getConfiguration = clzIActMag.getDeclaredMethod("getConfiguration"); Configuration config = (Configuration) mtdIActMag$getConfiguration.invoke(objIActMag); config.locale = locale; // iActMag.updateConfiguration(config); // 此处需要声明权限:android.permission.CHANGE_CONFIGURATION // 会重新调用 onCreate(); Class[] clzParams = { Configuration.class }; Method mtdIActMag$updateConfiguration = clzIActMag.getDeclaredMethod( "updateConfiguration", clzParams); mtdIActMag$updateConfiguration.invoke(objIActMag, config); } catch (Exception e) { e.printStackTrace(); } } 复制代码 实际运行后,发现对当前系统设置了新的Locale后,不单自己的应用语系改变了,系统所有的应用语系都改变了。这肯定是不合理的。有一个解决办法是在应用界面退出前再次对系统设置成碑的Locale,不过个人不喜欢这样的办法,加之调用updateConfiguration()方法后,整个Activity会重新onCreate(),这个考虑Activity的生命周期可有点费劲了。于是有了下面这第三种方法。 3. 自己转换语系(哈哈,这个名字很现实啊) 动手实现嘛,啥都系统弄好了,那程序员的存在还有什么意义呢。 自己转换语系有点麻烦,先看工程结构图: values/strings.xml与xml/english.xml的内容是相同的;values-zh-rCN/strings.xml与xml/chinese.xml的内容也是相同的。出现这样的冗余是因为生成APK时values下的内容都打到rasc去了,读取不了了。 自己实现语系的转换需要考虑到: 3.1 R.xxxxx.id与对应语系中文本串的对应(需要特别考虑到R.array.string字符串数组)。 3.2 解析xml。 3.3 设置语系后,所有界面元素的手动刷新。 在xml中声明一个string是这个的格式: 语言应用 复制代码 对应R文件会生成一个id指代该string public static final class string { public static final int app_name=0x7f050001; } 复制代码 3.1的问题就是如何实现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简单些。具体细节见LanguageApp_Sodino工程中的代码吧。 3.3 手动刷新界面。 要获取所有涉及到语系更新组件的索引逐一更新,体力活儿,细心点花点力气也可实现。 详细实现过程见下面三个工程中: (PS:不要问我为什么下载的工程在IDE中为什么无法直接使用,为什么打开是乱码红叉一大堆,既然是程序员,遇到问题是不是也该自己多思考思考呢。) 转载于http://www.devdiv.com/article-2274-1.html |
Android对国际化与多语言切换已经做得不错了,一个应用只要命名相应语系的values-[language]文件夹,通过“设置”→“语言&键盘”→“选择语言”即可实现应用多种语言的切换。 但如何在应用里自己实现?搜索过发现网上有如下的做法: Resources res = getResources(); Configuration config = res.getConfiguration(); config.locale = locale; DisplayMetrics dm = res.getDisplayMetrics(); res.updateConfiguration(config, dm); 复制代码 亲测,不成功。好吧,程序员又到了自力更生的时候了。下面开始讲应用多语言切换的三种方法。 先上效果图: 前两种方法的原理即在应用里实现“选择语言”。通过查看源码,其核心代码为: IActivityManager iActMag = ActivityManagerNative.getDefault(); try { Configuration config = iActMag.getConfiguration(); config.locale = locale; // 此处需要声明权限:android.permission.CHANGE_CONFIGURATION // 会重新调用 onCreate(); iActMag.updateConfiguration(config); } catch (RemoteException e) { e.printStackTrace(); } PS:感谢 曾阳 的帮助。 复制代码 可以发现IActivityManager与ActivityManagerNative都是非公开类。如何调用?第一种是API欺骗,第二种是使用Java反射机制。 1. API欺骗 烧制到手机中的android.jar包含了Android所需的各种类与方法;而供开发者使用的android.jar只是其中的一部分。API欺骗是指在应用中去模拟未公开的类和方法让应用编译通过并生成APK,然而在应用实际运行中调用的却仍是烧制到手机中真实的android.jar。 通过核心代码可以看到我们要模拟的是ActivityManagerNative中的一个方法getDefault()和IActivityManager中的两个方法getConfiguration()与updateConfiguration(config)。参照源码,应用的工程结构图及代码模拟如下: 工程结构图: 代码: ActivityManagerNative.java package android.app; /** * @author Sodino E-mail:sodinoopen@hotmail.com * @version Time:2011-7-10 上午11:37:01 */ public abstract class ActivityManagerNative { public static IActivityManager getDefault() { return null; } } IActivityManager.java package android.app; import android.content.res.Configuration; import android.os.RemoteException; /** * @author Sodino E-mail:sodinoopen@hotmail.com * @version Time:2011-7-10 上午11:37:46 */ public abstract interface IActivityManager { public abstract Configuration getConfiguration() throws RemoteException; public abstract void updateConfiguration(Configuration paramConfiguration) throws RemoteException; } 复制代码 实现模拟了这两个类后,即可正常使用上面提到的转换语系的核心代码了。 2. Java反射机制 不多说了,Java反射机制入门教程: http://java.sun.com/developer/te ... flection/index.html 之前写过的几个使用Java反射的例子: [Android]获取未安装的APK图标(原创非转帖) /article/2637265.html [Android]挂断、接听电话 /article/2637263.html 直接上代码: 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"); // IActivityManager iActMag = ActivityManagerNative.getDefault(); objIActMag = mtdActMagNative$getDefault.invoke(clzActMagNative); // Configuration config = iActMag.getConfiguration(); Method mtdIActMag$getConfiguration = clzIActMag.getDeclaredMethod("getConfiguration"); Configuration config = (Configuration) mtdIActMag$getConfiguration.invoke(objIActMag); config.locale = locale; // iActMag.updateConfiguration(config); // 此处需要声明权限:android.permission.CHANGE_CONFIGURATION // 会重新调用 onCreate(); Class[] clzParams = { Configuration.class }; Method mtdIActMag$updateConfiguration = clzIActMag.getDeclaredMethod( "updateConfiguration", clzParams); mtdIActMag$updateConfiguration.invoke(objIActMag, config); } catch (Exception e) { e.printStackTrace(); } } 复制代码 实际运行后,发现对当前系统设置了新的Locale后,不单自己的应用语系改变了,系统所有的应用语系都改变了。这肯定是不合理的。有一个解决办法是在应用界面退出前再次对系统设置成碑的Locale,不过个人不喜欢这样的办法,加之调用updateConfiguration()方法后,整个Activity会重新onCreate(),这个考虑Activity的生命周期可有点费劲了。于是有了下面这第三种方法。 3. 自己转换语系(哈哈,这个名字很现实啊) 动手实现嘛,啥都系统弄好了,那程序员的存在还有什么意义呢。 自己转换语系有点麻烦,先看工程结构图: values/strings.xml与xml/english.xml的内容是相同的;values-zh-rCN/strings.xml与xml/chinese.xml的内容也是相同的。出现这样的冗余是因为生成APK时values下的内容都打到rasc去了,读取不了了。 自己实现语系的转换需要考虑到: 3.1 R.xxxxx.id与对应语系中文本串的对应(需要特别考虑到R.array.string字符串数组)。 3.2 解析xml。 3.3 设置语系后,所有界面元素的手动刷新。 在xml中声明一个string是这个的格式: 语言应用 复制代码 对应R文件会生成一个id指代该string public static final class string { public static final int app_name=0x7f050001; } 复制代码 3.1的问题就是如何实现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简单些。具体细节见LanguageApp_Sodino工程中的代码吧。 3.3 手动刷新界面。 要获取所有涉及到语系更新组件的索引逐一更新,体力活儿,细心点花点力气也可实现。 详细实现过程见下面三个工程中: (PS:不要问我为什么下载的工程在IDE中为什么无法直接使用,为什么打开是乱码红叉一大堆,既然是程序员,遇到问题是不是也该自己多思考思考呢。) 转载于http://www.devdiv.com/article-2274-1.html |
相关文章推荐
- Android开发之Menu:OptionMenu(选项菜单)、ContextMenu(上下文菜单)、SubMenu(子菜单)
- Android开发之Menu:OptionMenu(选项菜单)、ContextMenu(上下文菜单)、SubMenu(子菜单)
- Android中Intent传递对象的两种方法(Serializable,Parcelable)!
- Android-ConvenientBanner轻松实现广告头效果
- Android 最火框架XUtils之注解机制详解
- Android 监听联系人的变化
- android中如何执行java命令
- Android SharedPreferences一般的读写 的用法。
- android:layout_weight的真实含义
- android 中如何限制 EditText 最大输入字符数
- Android 5.0 Changes
- Android中Fragment之间的通信和参数传递
- Android USER 版本与ENG 版本的差异--MTK官方解释
- android 百度地图
- Android 各层中日志打印功能的应用
- android很多图片做成帧动画造成内存溢出的解决方法。
- android-eclips中logcat不显示信息的问题解决
- Android CursorAdapter的使用
- Android 实现平滑滚动的歌词控件
- Android代码规范