Android JNI反射调用Java构造方法、成员方法和静态方法

Android开发中一般讲Java接口调用放在APP层,但是如果想对外隐藏Java接口调用,应该怎么办呢?我们可以将接口调用放在JNI层,通过反射调用所需接口,之后打包成.so库,这样既可对外隐藏所有调用细节。下面开始讲解JNI怎么调用Java方法。
首先模拟实现一个类,代表想隐藏的接口

代码如下:
 

package com.lb6905.jnidemo; 
import android.util.Log; 

public class TestClass { 
    private final static String TAG = "TestClass"; 
    public TestClass(){ 
    Log.i(TAG, "TestClass"); 
    } 

    public void test(int index) { 
    Log.i(TAG, "test : " + index); 
    } 

    public static void testStatic(String str) { 
    Log.i(TAG, "testStatic : " + str); 
    } 

    public static class InnerClass { 
        private int num; 

        public InnerClass() { 
        Log.i(TAG, "InnerClass"); 
        } 

        public void setInt(int n) { 
            num = n; 
            Log.i(TAG, "setInt: num = " + num); 
        } 
    } 
}

这个类包一个构造方法、一个成员方法,一个静态方法,一个内部类,大多数的类都是由这三种方法组成的。下面要做的就是怎么在JNI调用这些方法。
查看方法签名

这里首先Make Project,否则不会生成Java文件对应的class文件

进入到classpath目录下:
命令: cd app/build/intermediates/classes/debug

查看外部类的签名
javap -s -p com.lb6905.jnidemo.TestClass

查看内部类的签名
javap -s -p com.lb6905.jnidemo.TestClass$InnerClass

结果如下:
 

    F:\Apps\jniDemo\JNIDemo\app\build\intermediates\classes\debug>javap -s -p com.lb6905.jnidemo.TestClass
    Compiled from "TestClass.java"

    public class com.lb6905.jnidemo.TestClass

    {
        private static final java.lang.String TAG;
        descriptor:
        Ljava / lang / String; public com.lb6905.jnidemo.TestClass();
        descriptor:
        () V public void test ( int);
        descriptor:
        (I) V public static void testStatic (java.lang.String);
        descriptor:
        (Ljava / lang / String;)V
    }

    F:\Apps\jniDemo\JNIDemo\app\build\intermediates\classes\debug>javap -s -p com.lb6905.jnidemo.TestClass$InnerClass 
     Compiled from "TestClass.java"

    public class com.lb6905.jnidemo.TestClass$InnerClass

    {
        private int num;
        descriptor:
        I public com.lb6905.jnidemo.TestClass$InnerClass();
        descriptor:
        () V public void setInt ( int);
        descriptor:
        (I) V
    }

在JNI中反射调用上述方法

    注意:①. 在反射类时,使用(*env)->FindClass(env, "包名/类名");

                    若使用(*env)->GetObjectClass(env, clazz); , 有时获取不到方法名

               ②. 若回调java中非静态方法,调用(*env)->CallVoidMethod(); 之前需要根据构造方法实例化类对象

    JNIEXPORT void JNICALL Java_com_lb6905_jnidemo_MainActivity_JNIReflect(JNIEnv *env, jobject thiz) { 
        //实例化Test类 
        jclass testclass = (*env)->FindClass(env, "com/lb6905/jnidemo/TestClass");
        
        //构造函数的方法名为 
        jmethodID testcontruct = (*env)->GetMethodID(env, testclass, "", "()V"); 
        
        //根据构造函数实例化对象 
        jobject testobject = (*env)->NewObject(env, testclass, testcontruct); 
        
        //调用成员方法,需使用jobject对象 
        jmethodID test = (*env)->GetMethodID(env, testclass, "test", "(I)V"); 
        (*env)->CallVoidMethod(env, testobject, test, 1); 
        
        //调用静态方法 
        jmethodID testStatic = (*env)->GetStaticMethodID(env, testclass, "testStatic", "(Ljava/lang/String;)V"); 
        
        //创建字符串,不能在CallStaticVoidMethod中直接使用"hello world!",会报错的 
        jstring str = (*env)->NewStringUTF(env, "hello world!"); 
        
        // 调用静态方法使用的是jclass,而不是jobject 
        (*env)->CallStaticVoidMethod(env, testclass, testStatic, str); 
        
        // 实例化InnerClass子类 
        jclass innerclass = (*env)->FindClass(env, "com/lb6905/jnidemo/TestClass$InnerClass"); 
        jmethodID innercontruct = (*env)->GetMethodID(env, innerclass, "", "()V"); 
        jobject innerobject = (*env)->NewObject(env, innerclass, innercontruct); 
        
        // 调用子类的成员方法 
        jmethodID setInt = (*env)->GetMethodID(env, innerclass, "setInt", "(I)V"); 
        (*env)->CallVoidMethod(env, innerobject, setInt, 2); 
    }

 

参考于:https://blog.csdn.net/lb377463323/article/details/75303125

你可能感兴趣的:(Android)