风也温柔

计算机科学知识库

jni中调用java方法 Android Studio NDK开发-JNI调用Java方法

  相对于NDK来说SDK里面有更多API可以调用,有时候我们在做NDK开发的时候,需要在JNI直接Java中的方法和变量,比如,系统信息等....

  如何在JNI中调用Java方法呢?就需要先了解和了。

  和

  在JNI中可以通过可以找到Java类,得到,例如:

   clz=(*env)->(envjni中调用java方法 Android Studio NDK开发-JNI调用Java方法,"com/jjz/");

  的第二个参数需要传入类的完整包名。

  使用可以获取类的方法,得到,例如:

   =(*env)->(env,class,"","()V");

  如果调用的是静态方法需要使用获取。通过可以在JNI中找到需要调用的类,可以找到对应的方法,这样就可以在JNI中调用Java的方法了。

  在中,第四个参数是()V,这个是方法签名。那么方法签名的规则又是怎么样呢?

  方法签名

  在中第四个参数()V就是方法签名,Java是支持重载的jni中调用java方法,所以需要标明方法的传参和返回值,这就是方法的签名。它是用来保证方法的唯一性。其中()代表不传参数,V代表返回值为void。

  方法签名对于Java的类型都有一一对应的值。方法签名中用大写的字母对应了java的基本数据类型:

  其实就是有两个比较特殊的:对应的是Z,long对应的J,其他的都是首个字母的大写即可。

  数组的表示方法,以[为标志,一个[标识一维数组,[[表示二维数组,例如:

  引用类型的表示方法,需要以L开头,以;结束,中间对应类型的包名加类名,例如:

  自定义类的表示方法,比如包名为jjz.,类名为的表示方法:

  除了手动输入类名和方法签名以外,JDK还提供了直接生成方法签名的工具javap。

  在build之后可以在路径../app/build///debug下可以找到build之后生成的.class文件,运行命令:

  javap -s com/jjz/

  java jni调用dll文件_java 通过jni 调用c_jni中调用java方法

  就可以得到这个类的所有的方法签名:

  <pre class="hljs cpp">`Compiled from "JniHandle.java"
public class com.jjz.JniHandle { public com.jjz.JniHandle(); descriptor: ()V public static java.lang.String getStringFromStatic(); descriptor: ()Ljava/lang/String; public java.lang.String getStringForJava(); descriptor: ()Ljava/lang/String; } `</pre>

  有了类的引用,和方法的签名就可以直接在JNI中调用Java方法了,下面分别介绍下静态方法和类方法的调用。

  静态方法的调用

  调用类的静态方法jni中调用java方法,首先要得到类的引用,再调用类的静态方法。

  先定义一个类和静态方法用来提供给JNI调用:

  <pre class="hljs cpp">`public class JniHandle {
<p>java 通过jni 调用c_jni中调用java方法_java jni调用dll文件

public static String getStringFromStatic() { return "string from static method in java"; } } `</pre></p>

  在定义了一个方法com.jjz..,生成这个方法的JNI代码,在JNI代码中调用类的静态方法:

  <pre class="hljs php">`JNIEXPORT void JNICALL
Java_com_jjz_NativeUtil_callJavaStaticMethodFromJni(JNIEnv *env, jclass type) {

jclass jniHandle = (*env)->FindClass(env, "com/jjz/JniHandle");
if (NULL == jniHandle) {
    LOGW("can't find JniHandle");
    return; } jmethodID getStringFromStatic = (*env)->GetStaticMethodID(env, jniHandle, "getStringFromStatic", "()Ljava/lang/String;"); if (NULL == getStringFromStatic) { (*env)->DeleteLocalRef(env, jniHandle); LOGW("can't find method getStringFromStatic from JniHandle "); return; } jstring result = (*env)->CallStaticObjectMethod(env, jniHandle, getStringFromStatic); const char *resultChar = (*env)->GetStringUTFChars(env, result, NULL); (*env)->DeleteLocalRef(env, jniHandle); (*env)->DeleteLocalRef(env, result); LOGW(resultChar); } `</pre>

  在Java中调用com.jjz..可以该方法可以在中看到 from in java,这样就完成了在JNI中调用了Java静态方法。

  类方法的调用

  调用类方法要更加的复杂一些,调用步骤:

  通过找到类通过得到构造函数通过调用构造函数得到一个类的实例通过得到需要调用的方法使用类的实例调用方法

  先定义一个类方法:

  <pre class="hljs cpp">`public class JniHandle {
public String getStringForJava() { return "string from method in java"; } } `</pre>

  再定义一个方法:com.jjz..i,生成该方法的JNI代码,在JMI代码中实现调用的类方法,代码如下:

  <pre class="hljs php">`
<p>java jni调用dll文件_java 通过jni 调用c_jni中调用java方法

JNIEXPORT void JNICALL
Java_com_jjz_NativeUtil_callJavaMethodFromJni(JNIEnv *env, jclass type) {

jclass jniHandle = (*env)->FindClass(env, "com/jjz/JniHandle");
if (NULL == jniHandle) {
    LOGW("can't find jniHandle");
    return; } jmethodID constructor = (*env)->GetMethodID(env, jniHandle, "", "()V"); if (NULL == constructor) { LOGW("can't constructor JniHandle"); return; } jobject jniHandleObject = (*env)->NewObject(env, jniHandle, constructor); if (NULL == jniHandleObject) { LOGW("can't new JniHandle"); return; } jmethodID getStringForJava = (*env)->GetMethodID(env, jniHandle, "getStringForJava", "()Ljava/lang/String;"); if (NULL == getStringForJava) { LOGW("can't find method of getStringForJava"); (*env)->DeleteLocalRef(env, jniHandle); (*env)->DeleteLocalRef(env, jniHandleObject); return; } jstring result = (*env)->CallObjectMethod(env, jniHandleObject, getStringForJava); const char *resultChar = (*env)->GetStringUTFChars(env, result, NULL); (*env)->DeleteLocalRef(env, jniHandle); (*env)->DeleteLocalRef(env, jniHandleObject); (*env)->DeleteLocalRef(env, result); LOGW(resultChar); } `</pre></p>

  调用方法com.jjz..i即可看到Java中的字符串传递给了JNI最后输出到了上。

  在上面的代码中有一个方法叫做,它的意思是释放局部引用, VM释放局部引用有两种方法:

  既然上面说了VM会自动释放引用为什么还需要手动释放呢?

  其实某些局部变量会阻止它所引用的对象被GC回收,它所引用的对象无法被GC回收,自己本身也就无法被自动释放,因此需要使用。而这里使用了JNI Local ,在JNI中引用了Java对象,如果不使用释放的话,引用无法回收,就会造成内存泄露。

  源代码地址:

  文章来源:https://www.codetd.com/article/5681436