获取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);
}
}
打印如下: