Java进阶知识-反射
而mMainThread对象是ActivityThread类型,该类是我们的主线程类,里面有有一个mInstrumentation成员变量,该成员变量属于Instrumentation类型。应用中所有的Activity都继承自一个BaseActivity基类,基类中实现一个startActivity方法,在该方法之前加上一句打印,那么所有startActivity的地方都调用基类中的方法。通过反射
获取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);
    }
}
打印如下:
更多推荐
 
 



所有评论(0)