Andoid NDK编程 3 - java与JNI的交互

通过前面的两篇文章的学习,相信基本的JNI开发对你来说已经不是什么难事。 但是有时候我们想在JNI中做更多的事情,比如回调Java类中的函数,那么今天我们就来看一下如何实现。

JNI调用Java层函数

对于如何从JNI中调用Java类中的方法,其实在jni.h中已经定义了一系列的函数来实现这一目的,以下是一些经常用到的函数:


使用JNIEnv指针获取Class对象:

// C version
jclass (*FindClass)(JNIEnv*, const char*); 

// C++ version
jclass FindClass(const char* name)

参数介绍:

  • name: 类的路径字符串, 如 “/your/package/name/ClassName” ;

另外还有一种替代方法是通过Java层传到JNI层的JObject来获取该object所代表的类:

jclass GetObjectClass(jobject obj)

获取Java类中定义的method方法:

在jni.h中找到方法

// C Version
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);

// C++ version
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)

参数介绍:

  • clazz: Java类的Class对象
  • name: 方法名
  • sig: Java方法的签名;

获取Java类中的静态方法:

// get static function id 
// C++ version
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)

参数介绍:

  • clazz: Java类的Class对象
  • name: 方法名
  • sig: Java方法的签名;

获取Java类中的成员变量:

// get field id
// C++ version
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)

参数介绍:

  • clazz: Java类的Class对象
  • name: 成员变量的名字
  • sig: 成员变量的签名,如String的成员变量签名是:”L/java/lang/String;”

获取Java类中的静态成员变量:

// get field id
// C++ version
jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)

参数介绍:

  • clazz: Java类的Class对象
  • name: 成员变量的名字
  • sig: 成员变量的签名,如String的成员变量签名是:”L/java/lang/String;”

构造一个Java类的对象:

// C++ version construct one java object
jobject NewObject(jclass clazz, jmethodID methodID, ...)

调用指定的构造函数, 构造函数的名字叫做
如下面的例子:

jmethodID mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
obj = (*env)->NewObject(env, cls, mid);

通过上面介绍的一系列API,我们可以在JNI中获取到Java类的名字,获取到类中的函数名字了,那么如何调用这些Java中的函数呢?请看下面的jni函数:

// 调用java 类中的返回值为void的某个函数
void CallVoidMethod(jobject obj, jmethodID methodID, …)

参数介绍:

  • obj: Java类的Class对象
  • methodID: 方法的ID,通过上面的GetMethodID()得到

这里使用的是CallVoidMethod方法调用,因为没有返回值,如果有返回值的话使用对应的方法,要用对应的Call函数:

// non-static java function call back from jni
CallVoidMethod                   
CallIntMethod                         
CallBooleanMethod              
CallByteMethod 

// Java static functions call back from jni   
CallStaticVoidMethod
CallStaticIntMethod
CallStaticBooleanMethod
CallStaticByteMethod

一个例子

接下来用个简单的例子来介展示一下如何使用上面提到的函数:

Java代码:

假设我们在Java层中定义了包名为com.test.jnisample, 类名为JniManager的java类,在类中定义了一个函数供JNI来调用:

public class JniManager {
    public void callFromJni_1() {
        Log.d(TAG, "Call from JNI: void func");
    }
}

JNI代码:

static void sayHello(JNIEnv *env, jobject obj, jlong handle) {
     // get class name
        char* className = "com/test/jnisample/JniManager";
     jclass javaClazz = env->FindClass(className);
     //jclass javaClazz = env->GetObjectClass(obj);

     if(javaClazz == 0) {
            LOGI("JNI", "Java class not found");
     } else {
          LOGI("JNI", "Java class found");
     }

    // get method id
        jmethodID method_1 = env->GetMethodID(javaClazz, "callFromJni_1", "()V");
     if((method_1 == 0)) {
       LOGI("JNI", "Method 1 not found");
    } else {
        LOGI("JNI", "found method 1");

        // Call java function
        env->CallVoidMethod(obj, method_1);
    }

}