获取Class对象
有三种方式获取Class对象:
根据类的完整包名获取Class
Class clazz = Class.forName(“com.example.xjp.demo.reflect.PersonInfo”);
根据类名直接获取Class
Class clazz = PersonInfo.class;
根据实例类的对象获取Class
PersonInfo personInfo = new PersonInfo();
Class clazz = personInfo.getClass();
创建实例
通过反射来生成对象主要有两种方式
1. 使用Class对象的newInstance()方法来创建Class对象对应类的实例
Class clazz = PersonInfo.class; PersonInfo personInfo = (PersonInfo) clazz.newInstance();2.使用Class对象的构造器来创建实例
Constructor constructor = clazz.getConstructor(PersonInfo.class); //有构造器来创建实例,可以传参数给newInstance(Object ... initargs) PersonInfo personInfo = (PersonInfo) constructor.newInstance();获取方法
通过反射,可以获取某个类中的所有方法,包括private,public,protect类型的方法
1. 获取类的所有申明的方法,包括public,private,protect类型的方法
Class clazz = PersonInfo.class; Method[] declaredMethods = clazz.getDeclaredMethods();2.获取类中所有public方法
Class clazz = PersonInfo.class; Method[] methods = clazz.getMethods();3.获取类中指定的public方法
Class clazz = PersonInfo.class; PersonInfo personInfo = (PersonInfo) clazz.newInstance(); //第一个参数是方法名,第二个参数是该方法参数的类型 Method method = clazz.getMethod("getName", String.class); //调用 PersonInfo 类中的 getName()方法 String name = (String) method.invoke(personInfo , "是最帅的");4.获取指定的private方法
Class clazz = PersonInfo.class; PersonInfo personInfo = (PersonInfo) clazz.newInstance(); //第一个参数是方法名,第二个参数是该方法参数的类型 Method method = clazz.getDeclaredMethod("getAge", Integer.class); //由于该方法是private的,所以需要设置访问权限 method.setAccessible(true); //调用PersonInfo 类中的 getAge()方法 int age = (int) method.invoke(personInfo, 18);获取类的成员变量
获取类中所有成员变量,包括public,private,protect类型
Field[] declaredFields = clazz.getDeclaredFields();
2.获取类中所有public类型的成员变量
Field[] fields = clazz.getFields();3.获取指定的成员变量,public类型
Field nameField = clazz.getField("mName"); //修改成员变量mName的值为Tom nameField.set(personInfo, "Tom"); //得到成员变量nName的值 String name = nameField.get(personInfo);4.获取指定的成员变量,private类型
//得到私有的成员变量 mAge Field ageField = clazz.getDeclaredField("mAge"); //设置其访问权限 ageField.setAccessible(true); //修改成员变量 mAge 的值 ageField.set(test, 23); //获取该成员变量的值 int age = (int) ageField.get(test); public class PersonInfo { private int mAge = 18; public String mName = "xjp"; private int getAge(int age) { return mAge; } public String getName(String msg) { return mName + ":" + msg; } }反射的应用场景
我们来做一个有意思的功能,在你的应用所有启动Activity之前的地方加一个打印,打印出一些相关信息。你可能会想到如下策略:
应用中所有的Activity都继承自一个BaseActivity基类,基类中实现一个startActivity方法,在该方法之前加上一句打印,那么所有startActivity的地方都调用基类中的方法。
但是有这么一种情况,项目已经进行了80%,大部分Activity已经写好了,好多Activity都不是继承自同一个BaseActivity基类,如果需要改的话,改动地方太多,改动大,不划算。那么有没有一种更好的办法,修改少,又能实现该功能的方法呢?
答案就是利用反射,在系统调用startActivity的地方换成我们自己的startActivity方法,这一招叫做偷梁换柱。那么怎么实现呢?
我们先找到Android系统的startActivity方法是怎么实现的,该方法是Context类的方法,而Context只是一个借口,其实现类是ContextImpl类,代码如下:
@Override public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is // generally not allowed, except if the caller specifies the task id the activity should // be launched in. if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options); }最终启动activity是有mMainThread对象的getInstrumentation()方法获取Instrumentation对象,然后由该对象调用execStartActivity()方法来启动activity。而mMainThread对象是ActivityThread类型,该类是我们的主线程类,里面有有一个mInstrumentation成员变量,该成员变量属于Instrumentation类型。
我们的思路是替换ActivityThread类总的mInstrumentation对象,使用我们自己的 Instrumentation对象。实现如下:
public class ProxyInstrumentation extends Instrumentation { private static final String TAG = "ProxyInstrumentation"; // ActivityThread中原始的对象, 保存起来 Instrumentation mBase; public ProxyInstrumentation(Instrumentation base) { mBase = base; } public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { // Hook之前, XXX到此一游! Log.d(TAG, " 执行了startActivity, 参数如下: " + "who = [" + who + "], " + " contextThread = [" + contextThread + "], token = [" + token + "], " + " target = [" + target + "], intent = [" + intent + "], requestCode = [" + requestCode + "], options = [" + options + "]"); // 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了. // 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法 try { Method execStartActivity = Instrumentation.class.getDeclaredMethod( "execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); execStartActivity.setAccessible(true); return (Instrumentation.ActivityResult) execStartActivity.invoke(mBase, who, contextThread, token, target, intent, requestCode, options); } catch (Exception e) { // 某该死的rom修改了 需要手动适配 throw new RuntimeException("do not support!!! pls adapt it"); } }然后通过反射拿到ActivityThread类中的mInstrumentation,代码如下:
public static void attachContext() throws Exception{ // 先获取到当前的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 拿到原始的 mInstrumentation字段 Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation"); mInstrumentationField.setAccessible(true); Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread); // 创建代理对象 Instrumentation evilInstrumentation = new ProxyInstrumentation(mInstrumentation); // 偷梁换柱 mInstrumentationField.set(currentActivityThread, evilInstrumentation); }然后在你应用的Application类中调用如上方法即可:
public class DemoApplication extends Application { private static final String TAG = "DemoApplication"; private static Context mContext; @Override public void onCreate() { super.onCreate(); mContext = DemoApplication.this.getApplicationContext(); String processName = mContext.getApplicationInfo().processName; Log.i(TAG, "onCreate: the processName=" + processName); } public static Context getContext(){ return mContext; } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); try { attachContext(); } catch (Exception e) { e.printStackTrace(); } } public static void attachContext() throws Exception{ // 先获取到当前的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 拿到原始的 mInstrumentation字段 Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation"); mInstrumentationField.setAccessible(true); Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread); // 创建代理对象 Instrumentation evilInstrumentation = new ProxyInstrumentation(mInstrumentation); // 偷梁换柱 mInstrumentationField.set(currentActivityThread, evilInstrumentation); } }打印如下: