android的反射机制及用Jni对java类的反射

平常在写java程序的时候,有时会用到一些方法,但是在IDE中调用不到这些方法,进入到源码中,会发现这些方法的上面有@hide标识,这表示这些是被隐藏的


image.png

还有在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放
如果想调用这些方法只能通过反射来进行调用

与Java反射相关的类如下:

序号 类名 用途
1 Class 代表类的实体,在运行的Java应用程序中表示类和接口
2 Field 代表类的成员变量
3 Method 代表类的方法
4 Constructor 代表类的构造方法

获得类相关的方法

序号 获取类方法 用途
1 asSubclass(Class<U> clazz) 把传递的类的对象转换成代表其子类的对象
2 Cast 把对象转换成代表类或是接口的对象
3 getClassLoader() 获得类的加载器
4 getClasses() 返回一个数组,数组中包含该类中所有公共类和接口类的对象
5 getDeclaredClasses() 返回一个数组,数组中包含该类中所有公共类和接口类的对象
6 forName(String className) 根据类名返回类的对象
7 getName() 获得类的完整路径名字
8 newInstance() 创建类的实例
9 getPackage() 获得类的包
10 getSimpleName() 获得类的名字
11 getSuperclass() 获得当前类继承的父类的名字
1 2 getInterfaces() 获得当前类实现的类或是接口

获得类构造器的方法

序号 构造方法 用途
1 getConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法
2 getConstructors() 获得该类的所有公有构造方法
3 getDeclaredConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的构造方法
4 getDeclaredConstructors() 获得该类所有构造方法

获得注解的方法

序号 获取注解方法 用途
1 getAnnotation(Class<A> annotationClass) 返回该类中与参数类型匹配的公有注解对象
2 getAnnotations() 返回该类所有的公有注解对象
3 getDeclaredAnnotation(Class<A> annotationClass) 返回该类中与参数类型匹配的所有注解对象
4 getDeclaredAnnotations() 返回该类所有的注解对象

获得属性的方法

序号 获取属性方法 用途
1 getField(String name) 获得某个公有的属性对象
2 getFields() 获得所有公有的属性对象
3 getDeclaredField(String name) 获得某个属性对象
4 getDeclaredFields() 获得所有属性对象

获得方法的方法

序号 获取属性方法 用途
1 getMethod(String name, Class...<?> parameterTypes) 获得该类某个公有的方法
2 getMethods() 获得该类所有公有的方法
3 getDeclaredMethod(String name, Class...<?> parameterTypes) 获得该类某个方法
4 getDeclaredMethods() 获得该类所有方法

栗子

创建一个对象类,,有两个构造方法,一个public方法 一个static方法 一个private方法

public class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public Person() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "toString=name="+name+",age="+age;
    }

    private String myToString(){
        return "myToString=name="+name+",age="+age;
    }

    public static String staticToString(){
        return "static.tostring";
    }

}

调用反射获取它

    private void reflex1() throws Exception{
        //获取类对象
        Class calzz=Class.forName("com.example.testreflex.Person");
        //获取无参构造方法
        Constructor constructor1=calzz.getConstructor();
        //获取类中变量
        Field name =calzz.getDeclaredField("name");
        Field age =calzz.getDeclaredField("age");
        //允许方位私有变量
        name.setAccessible(true);
        age.setAccessible(true);
       //用无参构造函数,实例化对象
        Object object1=constructor1.newInstance();
       //反射获得类中方法
        Method method=calzz.getMethod("setAge",age.getType());
        Method method2=calzz.getMethod("setName",name.getType());
        Method method3=calzz.getMethod("toString");
        //执行类中的set方法
        method.invoke(object1,10);
        method2.invoke(object1,"Hello");
        String text2= (String) method3.invoke(object1);
        //进行打印
        Log.i("info","person.tostring="+text2);
    }
    private void reflex2() throws Exception{
        //  获取类对象
        Class calzz=Class.forName("com.example.testreflex.Person");
        Field name =calzz.getDeclaredField("name");
        Field age =calzz.getDeclaredField("age");
        name.setAccessible(true);
        age.setAccessible(true);
        //获取有参的构造方法
        Constructor constructor2=calzz.getConstructor(age.getType(),name.getType());
        //实例化对象
        Object object2=constructor2.newInstance(11,"hello2");
        //获取类中的方法
        Method method3=calzz.getMethod("toString");
        //获取私有方法
        Method method4=calzz.getDeclaredMethod("myToString");
        method4.setAccessible(true);
        String text= (String) method3.invoke(object2);
        String test2= (String) method4.invoke(object2);
        Log.i("info","person2.tostring="+text);
        Log.i("info","person2.tostring="+test2);
       //获取静态方法
        Method method5=calzz.getDeclaredMethod("staticToString");
        String test3= (String) method5.invoke(method5);
        Log.i("info","person2.tostring="+test3);
    }

输出的打印日志

 I/info: person.tostring=toString=name=Hello,age=10
 I/info: person.tostring=toString=name=hello2,age=11
 I/info: person2.tostring=myToString=name=hello2,age=11
 I/info: person3.tostring=static.tostring

在实际工作中的例子,获取EthernetManager及EthernetDevInfo

EthernetManager.png

EthernetManager是个隐藏类,之前我们公司用的IP电话需要使用这个类,余是我就给他反射出来了
1.实例化EthernatManager

private Class<?> ethernetManager; //未实例化类对象
private Object objEthernetManager;//实例化对象
private Class<?> ethernetDevInfo;
private Object objEthernetDevInfo;

/** EthernetManager mEthManager = (EthernetManager)
 getSystemService(Context.ETHERNET_SERVICE); 
*这个是正常初始化调用的模式
*/
//在实际工作中获取类
ethernetManager = Class.forName("android.net.ethernet.EthernetManager");
//获取Contexct属性ETHERNET_SERVICE这个是被隐藏的
Class<?> context = Class.forName("android.content.Context");
            Field f = context.getField("ETHERNET_SERVICE");
            String ETHERNET_SERVICE = (String) f.get(context);
//实例化EthernetManager
            objEthernetManager = context
                    .getSystemService(ETHERNET_SERVICE);
//接下来需要实例化 EthernetDevInfo 这个类 下面是正常的调用方式
// EthernetDevInfo mInterfaceInfo = mEthManager.getSavedConfig();
//EthernetDevInfo通过EthernetManager的getSavedConfig()方法进行初始化
//获取EthernetManager中的getSavedConfig方法
Method getSavedConfig = ethernetManager
                    .getDeclaredMethod("getSavedConfig");
ethernetDevInfo = Class
                    .forName("android.net.ethernet.EthernetDevInfo");//获取EthernetDevInfo类
objEthernetDevInfo = getSavedConfig.invoke(objEthernetManager);//执行mEthManager.getSavedConfig()方法

调用SystemProperties的get set方法

SystemProperties同样也是系统的隐藏类,直接调用是调用不到的

            Class<?> mClassType = Class.forName("android.os.SystemProperties");
            Method getMethod = mClassType
                    .getDeclaredMethod("get", String.class);
            value = (String) getMethod.invoke(mClassType, key);
            Method getMethod = mClassType
                    .getDeclaredMethod("set", String.class);
            value = (String) getMethod.invoke(mClassType, key);

调用构造函数RouteInfo

准备调用的RouteInfo构造方法
            // 获得了路由信息类
            Class<?> routeInfoClass = Class.forName("android.net.RouteInfo");
            // 获得路由信息类的一个构造器,参数是网关
            Constructor<?> routeInfoConstructor = routeInfoClass
                    .getConstructor(new Class[] { InetAddress.class });
            // 生成指定网关的路由信息类对象
            Object routeInfo = routeInfoConstructor.newInstance(gateWay);

接下来我将用jni方法反射出person类中的属性和方法
jni方法的相关介绍请看我另一篇文章
[JNI方法的汇总](https://www.jianshu.com/p/4334f084b2a4
jni部分我直接上的代码,下面是我的目录结构

工程结构.png

利用javaP反射出person中的目录结构

javap -classpath E:\StudioProjects\TestReflex\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes -p -s com.example.testreflex.Person
Compiled from "Person.java"
public class com.example.testreflex.Person {
  private int age;
    descriptor: I
  private java.lang.String name;
    descriptor: Ljava/lang/String;
  private byte[] b;
    descriptor: [B
  public com.example.testreflex.Person(int, java.lang.String);
    descriptor: (ILjava/lang/String;)V

  public com.example.testreflex.Person();
    descriptor: ()V

  public int getAge();
    descriptor: ()I

  public void setAge(int);
    descriptor: (I)V

  public java.lang.String getName();
    descriptor: ()Ljava/lang/String;

  public void setName(java.lang.String);
    descriptor: (Ljava/lang/String;)V

  public java.lang.String toString();
    descriptor: ()Ljava/lang/String;

  private java.lang.String myToString();
    descriptor: ()Ljava/lang/String;

  public static java.lang.String staticToString();
    descriptor: ()Ljava/lang/String;

  public byte[] getB();
    descriptor: ()[B

  public void setB(byte[]);
    descriptor: ([B)V
}

Process finished with exit code 0

在NDKReflex.java文件中创建 reflex1 reflex2 reflex3三个方法
reflex1 为创建无参构造函数 然后利用set方法设置属性,最后输出
reflex2 为创建有参构造函数然后输出
reflex3 为创建无参构造函数 获取person中的变量,最后输出修改变量后的方法

public class NDKReflex {

    static {
        System.loadLibrary("reflex-lib");
    }

    public native void reflex1();
    public native void reflex2();
    public native void reflex3();
}

reflex1方法 创建无参构造函数 然后利用set方法设置属性,最后输出

  void printJstring(JNIEnv *env,jstring string){
      const char * c_name = env->GetStringUTFChars(reinterpret_cast<jstring>(string), NULL);//转换成 char *
      LOGI("%s",c_name);
}

JNIEXPORT void JNICALL Java_com_example_testreflex_NDKReflex_reflex1
  (JNIEnv *env, jobject object){
      jclass clazz=env->FindClass("com/example/testreflex/Person");
      //"<init>"为构造方法标识
        LOGI("reflex1");
      jmethodID init1 = env->GetMethodID(clazz, "<init>", "()V");//无参构造函数
      jmethodID setAge= env->GetMethodID(clazz , "setAge","(I)V");//获取setAge方法
      jmethodID setName= env->GetMethodID(clazz , "setName","(Ljava/lang/String;)V");//设置setName方法
      jmethodID toString= env->GetMethodID(clazz , "toString","()Ljava/lang/String;");//获取toString方法
      jmethodID myToString= env->GetMethodID(clazz , "myToString","()Ljava/lang/String;");//获取myToString方法
      jmethodID staticToString= env->GetStaticMethodID(clazz , "staticToString","()Ljava/lang/String;");//获取静态staticToString方法
      jobject obj=env->NewObject(clazz,init1);//实例化Person对象
      env->CallVoidMethod(obj,setAge,10);//person.setAge(10)
      env->CallVoidMethod(obj,setName,env->NewStringUTF("I"));//person.setName("I")
    jstring jtoString= reinterpret_cast<jstring>(env->CallObjectMethod(obj, toString));
    printJstring(env,jtoString);//toString
    jstring jMyString= reinterpret_cast<jstring>(env->CallObjectMethod(obj, myToString));
    printJstring(env,jMyString);//myToString
    jstring jstaticToString= reinterpret_cast<jstring>(env->CallStaticObjectMethod(clazz, staticToString));
    printJstring(env,jstaticToString);//saticToString
  }

reflex2方法 创建有参构造函数然后输出

JNIEXPORT void JNICALL Java_com_example_testreflex_NDKReflex_reflex2
  (JNIEnv *env, jobject object){
    jclass clazz=env->FindClass("com/example/testreflex/Person");
    //"<init>"为构造方法标识
    LOGI("reflex2");
    jmethodID init1 = env->GetMethodID(clazz, "<init>", "(ILjava/lang/String;)V");//有参构造函数 int String
    jmethodID toString= env->GetMethodID(clazz , "toString","()Ljava/lang/String;");//获取toString方法
    jmethodID myToString= env->GetMethodID(clazz , "myToString","()Ljava/lang/String;");//获取myToString方法
    jmethodID staticToString= env->GetStaticMethodID(clazz , "staticToString","()Ljava/lang/String;");//获取静态staticToString方法
    jobject obj=env->NewObject(clazz,init1,43,env->NewStringUTF("My Father"));//new Person(43,"My Father")
    jstring jtoString= reinterpret_cast<jstring>(env->CallObjectMethod(obj, toString));
    printJstring(env,jtoString);//toString
    jstring jMyString= reinterpret_cast<jstring>(env->CallObjectMethod(obj, myToString));
    printJstring(env,jMyString);//myToString
    jstring jstaticToString= reinterpret_cast<jstring>(env->CallStaticObjectMethod(clazz, staticToString));
    printJstring(env,jstaticToString);//saticToString
  }

reflex3方法 创建无参构造函数 获取person中的变量,最后输出修改变量后的方法

JNIEXPORT void JNICALL Java_com_example_testreflex_NDKReflex_reflex3
        (JNIEnv *env, jobject object){
    jclass clazz=env->FindClass("com/example/testreflex/Person");
    //"<init>"为构造方法标识
    LOGI("reflex3");
    jmethodID init1 = env->GetMethodID(clazz, "<init>", "()V");//无参构造函数
    jfieldID age=env->GetFieldID(clazz,"age","I");
    jfieldID name=env->GetFieldID(clazz,"name","Ljava/lang/String;");
    jmethodID toString= env->GetMethodID(clazz , "toString","()Ljava/lang/String;");//获取toString方法
    jmethodID myToString= env->GetMethodID(clazz , "myToString","()Ljava/lang/String;");//获取myToString方法
    jmethodID staticToString= env->GetStaticMethodID(clazz , "staticToString","()Ljava/lang/String;");//获取静态staticToString方法
    jobject obj=env->NewObject(clazz,init1);//new Person()
    env->SetIntField(obj,age,40); //age = 40;
    env->SetObjectField(obj,name,env->NewStringUTF("My Mother"));//name=My Mother
    jstring jtoString= reinterpret_cast<jstring>(env->CallObjectMethod(obj, toString));
    printJstring(env,jtoString);//toString
    jstring jMyString= reinterpret_cast<jstring>(env->CallObjectMethod(obj, myToString));
    printJstring(env,jMyString);//myToString
    jstring jstaticToString= reinterpret_cast<jstring>(env->CallStaticObjectMethod(clazz, staticToString));
    printJstring(env,jstaticToString);//saticToString
}

输出的结果

10-03 13:44:19.991 5847-5847/com.example.testreflex I/myReflex-jni: reflex1
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: toString mothed,name=I,age=10
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: myToString mothed,name=I,age=10
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: static.tostring
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: reflex2
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: toString mothed,name=My Father,age=43
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: myToString mothed,name=My Father,age=43
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: static.tostring
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: reflex3
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: toString mothed,name=My Mother,age=40
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: myToString mothed,name=My Mother,age=40
10-03 13:44:19.992 5847-5847/com.example.testreflex I/myReflex-jni: static.tostring
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • 整体Retrofit内容如下: 1、Retrofit解析1之前哨站——理解RESTful 2、Retrofit解析...
    隔壁老李头阅读 4,568评论 2 12
  • 一、基础知识:1、JVM、JRE和JDK的区别:JVM(Java Virtual Machine):java虚拟机...
    杀小贼阅读 2,373评论 0 4
  • 整理来自互联网 1,JDK:Java Development Kit,java的开发和运行环境,java的开发工具...
    Ncompass阅读 1,537评论 0 6
  • 一:java概述: 1,JDK:Java Development Kit,java的开发和运行环境,java的开发...
    慕容小伟阅读 1,778评论 0 10