Access Internal classes in Android

So long before, i was blogging too much, then i stopped and told you guys i'll be doing more Android and Frontend, then i quit OpenRice and now back to doing Android as hobby. 

Senario
Say you want to access some internal class in Android which are not public and you really want to use that feature, what do you do?

Notes
* This is on how to access non public classes thus are restricted classes which are not recommended, use this knowledge at your own risk and i'm NOT to be held responsible to any harm it might cause.
* This is quite advance and i hope to explain it as much as possible while keeping it simple.
* The following codes might not work as you/I expected it to work thus this is for reference only

Introduction
As i was trying to update my  tutorial on Localization  i was looking at how the locale setting was done and i found it at the  LocalePicker.java  under the  onListItemClick  function, if you try this code in your app you would realized that ActivityManagerNative and IActivityManager are internal classes. So how to use this piece of code?
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
Loc loc = mLocales[position];
config.locale = loc.locale;
final String language = loc.locale.getLanguage();
final String region = loc.locale.getCountry();
am.updateConfiguration(config);



What Do I Need
In Manafiest.xml
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />

Codes
DexFile  df = new DexFile(new File("/system/app/Settings.apk"));
String name = "com.android.settings.LocalePicker"; 
ClassLoader cl = getClassLoader();     
Class LocalePicker = df.loadClass(name, cl);
Class ActivityManagerNative = Class.forName("android.app.ActivityManagerNative");
Class IActivityManager = Class.forName("android.app.IActivityManager");

Method getDefault =  ActivityManagerNative.getMethod("getDefault", null);
Object am = IActivityManager.cast(getDefault.invoke(ActivityManagerNative, null));

Method getConfiguration =  am.getClass().getMethod("getConfiguration", null);

Configuration config = (Configuration) getConfiguration.invoke(am, null);
Locale locale = new Locale(languageToLoad);
Locale.setDefault(locale);
config.locale = locale;

Class[] args = new Class[1];
args[0] = Configuration.class;
Method updateConfiguration =  am.getClass().getMethod("updateConfiguration", args);
updateConfiguration.invoke(am, config);


Explanation
DexFile df = new DexFile(new File("/system/app/Settings.apk"));
You need to load the package from where the class is from 
NOTE: The dex don't directly open or readed in this line but they're memory-mapped read-only by the VM.

String name = "com.android.settings.LocalePicker"; 
ClassLoader cl = getClassLoader(); 
Class LocalePicker = df.loadClass(name, cl);

Load the class where your target classes were imported. 

Class ActivityManagerNative = Class.forName("android.app.ActivityManagerNative");
Class IActivityManager = Class.forName("android.app.IActivityManager");

Load the classes you want to use

Method getDefault = ActivityManagerNative.getMethod("getDefault", null);
To use a method you have to get the method first. http://developer.android.com/reference/java/lang/reflect/Method.html

Object am = IActivityManager.cast(getDefault.invoke(ActivityManagerNative, null));
Execute the getDefault method using the invoke class, and since it has a return of IActivityManager, cast the returned object.

Method getConfiguration = am.getClass().getMethod("getConfiguration", null);
Since  am  is an instance of IActivityManager we could use getClass() to reflect on its method.

Configuration config = (Configuration) getConfiguration.invoke(am, null);
Execute the getConfiguration method, you can see here we use the regular casting (Configuration), you could also use Configuration.Class.cast

ocale locale = new Locale(languageString);
Locale.setDefault(locale);
config.locale = locale;

Just set a new Locale configuration

Class[] args = new Class[1];
args[0] = Configuration.class;
Method updateConfiguration = am.getClass().getMethod("updateConfiguration", args);
updateConfiguration.invoke(am, config);

To reflect on a method with arguements you have to do it this way, the second argument is a list of Class type of the required arguments. Optionally you could refer to another approach found in  Calling private methods in Android

Conclusion
Reflecting a class could be lead to unknown results, like this class would set System Wide language change and not application wise and thus executing would lead to your whole system being reconfigured on the language (your home screen would HANG after you change and click Home) , this is a powerful tool thus "We great knowledge comes great responsibility" :)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值