美文网首页
JNI开发的一般流程

JNI开发的一般流程

作者: 漫游之光 | 来源:发表于2018-11-07 20:48 被阅读0次
  • 定义好本地的native方法。
  • javah生成xxx.h头文件。
  • 拷贝xxx.h个jni.h和jni_md.h文件添加到C++工程中。
  • 实现xxx.h头文件中定义的native方法。
  • 生成dll动态库,java引入.dll动态库运行即可。

下面以一个例子来说明这个流程,先在java层中定义好方法:

package jni;

public class JniExample {
    
    public static void main(String[] args) {
        JniExample example = new JniExample();
        example.setField();
        System.out.println("name = "+name+"\tage = "+example.age);
        example.callMethod();
        System.out.println("name = "+name+"\tage = "+example.age);
        Point point = getPoint();
        System.out.println("x = "+point.getX()+"\ty = "+point.getY());
    }
    
    public static String name = "abcdef";
    private int age = 18;
        
    public void setField(String name,int age) {
        this.name = name;
        this.age = age;
    }
    
    public native void setField();
    
    public native void callMethod();
    
    public static native Point getPoint(); 
    
    static {
        System.load("G:\\Workspace\\VSCode\\jni\\jni.dll");
    }
}

package jni;

public class Point {
    private int x;
    private int y;
    
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    public int getX() {
        return x;
    }
    
    public int getY() {
        return y;
    }
}

可以看出,定义了3个本地方法。在我看来,java和本地方法之间进行绑定,很依赖字符串,所以,在C/C++中会使用很多常量字符串,用于寻找java的类,和java中定义的属性和方法。所以,在这里定义的本地方法,在C/C++中并不是这个名称,需要带上一串前缀,用于标识类。规则这里就不说了,因为一般我们使用下面的命令来生成头文件,这个命令要在src目录下输入。

javah -classpath . -jni jni.JniExample

这样,就会生成一个头文件jni_JniExample.h:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class jni_JniExample */

#ifndef _Included_jni_JniExample
#define _Included_jni_JniExample
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     jni_JniExample
 * Method:    setField
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_jni_JniExample_setField
  (JNIEnv *, jobject);

/*
 * Class:     jni_JniExample
 * Method:    callMethod
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_jni_JniExample_callMethod
  (JNIEnv *, jobject);

/*
 * Class:     jni_JniExample
 * Method:    getPoint
 * Signature: ()Ljni/Point;
 */
JNIEXPORT jobject JNICALL Java_jni_JniExample_getPoint
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

值得注意的是,原来生成的#include<jni.h>,如果没有把jni.h的路径加入到系统头文件的路径的话,需要把两个头文件使用双引号引入,然后手工把这两个头文件拷贝到工程目录下。

然后,就可以创建一个同名的C文件,然后实现头文件中定义的方法:

#include <string.h>
#include "jni_JniExample.h"

JNIEXPORT void JNICALL Java_jni_JniExample_setField(JNIEnv *env, jobject jobj){

  jclass j_class = (*env)->GetObjectClass(env, jobj);
  jfieldID field_id = (*env)->GetStaticFieldID(env, j_class, "name", "Ljava/lang/String;");
  jstring name = (*env)->GetStaticObjectField(env, j_class, field_id);
  const char *s = (*env)->GetStringUTFChars(env, name, NULL);
  char temp[strlen(s) + 1];
  strcpy(temp, s);
  char *p = temp;
  while (*p != '\0'){
    *p = *p + 1;
    p++;
  }

  jfieldID f1 = (*env)->GetFieldID(env, j_class, "age", "I");
  jint age = (*env)->GetIntField(env, jobj, f1);
  age += 1;
  (*env)->SetIntField(env, jobj, f1, age);
}

JNIEXPORT void JNICALL Java_jni_JniExample_callMethod(JNIEnv *env, jobject jobj){
  jclass jclz = (*env)->GetObjectClass(env, jobj);
  jmethodID method = (*env)->GetMethodID(env, jclz, "setField", "(Ljava/lang/String;I)V");
  jstring name = (*env)->NewStringUTF(env, "Micheal");
  jint age = 20;
  (*env)->CallVoidMethod(env, jobj, method, name, age);
}

JNIEXPORT jobject JNICALL Java_jni_JniExample_getPoint(JNIEnv *env, jclass jclz){
  jclass point = (*env)->FindClass(env,"jni/Point");
  jmethodID method = (*env)->GetMethodID(env,point,"<init>","(II)V");
  jobject ret = (*env)->NewObject(env,point,method,1,2);
  return ret;
}

这里实现了访问java类中的属性和方法。从代码中可以看出来,C/C++和java的类型转化主要通过env这个指针来完成的,env里面定义了许多函数,用来实现相互转化。在C/C++中看到的java的属性只有基本类型和Object类型,这里类型安全基本上是靠程序员来保证。因为java支持函数重载,所以如果要调用函数,不光要指定函数名,还需要指定类型签名,也就是要加上参数和返回值类型,不然无法区分重载函数。要获得签名,可以在bin目录下,使用下面的命令获得:

javap -classpath . -p -s jni.JniExample

要把写好的C/C++代码编译成动态库,可以使用下面的命令:

gcc -shared -fPIC jni_JniExample.c  -o jni.dll

然后,运行java程序,就发现可以调用本地方法啦。

相关文章

网友评论

      本文标题:JNI开发的一般流程

      本文链接:https://www.haomeiwen.com/subject/zkptxqtx.html