分享

JNI ( Java Native Interface ) for C/C++ with examples – I dont know zilch !

 oskycar 2022-04-14

使用 Java 编写应用程序时,有时仅 Java 无法满足应用程序的需求。可能想要使用标准 Java 类库中不存在的功能,或者可能只想使用以其他语言编写的现有库。这就是 JNI 的用武之地。

我发现大多数关于 JNI 的在线文档看起来都非常分散和过时。因此,这篇文章的范围是向您展示如何通过简单的示例来实现 JNI:

  • 用 C 编写 HelloWorld 并从 Java 调用它
  • 将整数和字符串从 C 传递到 Java
  • 将对象数组从 C 传递到 Java

同样也可以用 C++ 来实现。请注意下面第 4 步中提到的修改。

从Git克隆所有示例

 

来自 C 的 HelloWorld

1.编写Java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//HelloWorld.java
public class HelloWorld
{
  native void cfunction();//Declaring the native function
                             
  static
  {
     System.loadLibrary("forhelloworld");//Linking the native library
  }                                      //which we will be creating.
  public static void main(String args[])
  {
     HelloWorld obj = new HelloWorld();
     obj.cfunction();//Calling the native function
  }
}

2.编译Java代码,生成class文件

1
javac HelloWorld.java

3.从class文件生成一个Header文件

1
javah HelloWorld

这将生成一个文件 HelloWorld.h,其中包含:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//HelloWorld.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    cfunction
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_cfunction
  (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

3.从头文件中获取JNI函数签名

  1. 这两行组成了函数头:
1
2
JNIEXPORT void JNICALL Java_HelloWorld_cfunction
(JNIEnv *, jobject);

即使我们声明没有参数的本机函数,JNI 函数签名仍然包含 2 个参数,它们是:

  • JNIEnv * : 引用所有 JNI 函数的指针
  • jobject : this指针的等价物

4.使用函数签名编写本机代码

  • 添加头文件jni.h
  • 使用从上一步获得的函数签名,而不是 main() 函数。
  • 为 JNIEnv (env) 和 Jobject (jobj) 添加参数变量。
  • IMPORTANT :: If the native code is in C++, please note the only modification to be made is that the JNI functions should be called as env->func_name() instead of (*env)->func_name(). That is because C uses structures while C++ uses classes.
1
2
3
4
5
6
7
8
9
10
//HelloWorld.c
  
#include <jni.h>
#include <stdio.h>
  
JNIEXPORT void JNICALL Java_HelloWorld_cfunction
(JNIEnv *env, jobject jobj)
{
   printf("\n > C says HelloWorld !\n");
}

5. Generate the library file from the native code

1
gcc -o libforhelloworld.so -shared -fPIC -I (PATH TO jni.h header) HelloWorld.c -lc
  •     libforhelloworld.so is the name of the native library you are going to create, it should be named as “lib”+(the library name used in the load library statement within the java code)
  •     -fPIC is some sort optimization for loading the machine code into the RAM, gcc requested this flag to be set. Might not be required for all systems.

If you don’t specify the path correctly you will encounter this error :

1
2
3
4
HelloWorld.c:1:17: fatal error: jni.h: No such file or directory
 #include <jni.h>
                 ^
compilation terminated.

It is usually present inside

1
/usr/lib/jvm/default-java/include

or

1
/usr/lib/jvm/java-1.7.0-openjdk-amd64/include

depending upon the version of Java you have installed in your system.

6. Place the library file in the standard /usr/lib folder

If the previous command executed successfully, it would have generated a file libforhelloworld.so . Conventionally this library file need not be placed anywhere else, it needs to reside in the current working directory.

But for that to work you need to have set the JAVA_PATH variables correctly, in most cases they won’t be set correctly. An easy hack to this would be to just place it inside /usr/lib

1
sudo cp libforhelloworld.so /usr/lib

If you don’t place the library file, you would encounter this error :

1
2
3
4
5
Exception in thread "main" java.lang.UnsatisfiedLinkError: no forhelloworld in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1886)
        at java.lang.Runtime.loadLibrary0(Runtime.java:849)
        at java.lang.System.loadLibrary(System.java:1088)
        at HelloWorld.<clinit>(HelloWorld.java:9)

7. Execute the Java application

1
java HelloWorld

If you’ve followed all the steps correctly, C would greet the world 😀

1
> C says HelloWorld !

 

PASS INTEGERS FROM C TO JAVA

Example : Write a Java program to find the factorial of a number. Pass the number as an argument from Java to a native function in C which returns the factorial as an integer.

Java code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//factorial.java
  
import java.util.Scanner;
  
public class factorial
{
  native int fact(int num);
  
  static
  {
        System.loadLibrary("forfact");
  }
  
  public static void main(String args[])
  {
        Scanner inp = new Scanner(System.in);
  
        System.out.println(" > Enter number :: ");
        int num = inp.nextInt();
  
        factorial obj = new factorial();
  
        System.out.println(" > The factorial of "+num+" is "+obj.fact(num));
  }
}                                                                                                        

C代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//factorial.c
  
#include <jni.h>
#include <stdio.h>
  
JNIEXPORT jint JNICALL Java_factorial_fact
(JNIEnv *env, jobject jobj, jint num)
{
  jint result=1;
  
  while(num)
  {
        result*=num;
        num--;
  }
  
return result;
}

 

将字符串从 C 传递到 JAVA

示例:编写一个Java参数来给定给Java的字符串。将给定的字符串作为传递给C中的函数,函数返回给本机的字符串。

Java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//reverse.java
  
import java.util.Scanner;
  
public class reverse
{
  native String reversefunc(String word);
  
  static
  {
        System.loadLibrary("forreverse");
  }
  
  public static void main(String args[])
  {
        Scanner inp = new Scanner(System.in);
  
        System.out.println(" > Enter a string :: ");
        String word = inp.nextLine();
  
        reverse obj = new reverse();
  
        System.out.println(" > The reversed string is :: "+obj.reversefunc(word));
  }
}

C代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//reverse.c
  
#include <jni.h>
#include <stdio.h>
  
JNIEXPORT jstring JNICALL Java_reverse_reversefunc
(JNIEnv *env,jobject jobj,jstring original)
{
  const char *org;
  char *rev;
  
  org = (*env)->GetStringUTFChars(env,original,NULL);
  
  int i;
  int size = (*env)->GetStringUTFLength(env,original);
    
  for(i=0;i<size;i++)
        rev[i]=org[size-i-1];
  
  rev[size]='\0';
  
return (*env)->NewStringUTF(env,rev);
}

 

将志愿从 C 传递到 VA

示例:编写一个程序,生成参数n个波那契数前。“将n个”作为Java中的斐数作为本函数将波那契数作为本机返回。

Java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//fibonacci.java
  
import java.util.Scanner;
  
public class fibonacci
{
  native int[] returnfibo(int n);
  
  static
  {
        System.loadLibrary("fibonacci");
  }
  
  public static void main(String args[])
  {
        Scanner inp = new Scanner(System.in);
  
        System.out.println(" > Enter n :: ");
        int n = inp.nextInt();
  
        fibonacci obj = new fibonacci();
        int[] Fibo = obj.returnfibo(n);
  
        System.out.println(" > The first "+n+" fibonacci numbers are :: ");
  
        for(int i=0;i<n;i++)
          System.out.print(Fibo[i]+",");
  }
}

C代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//fibonacci.c
  
#include <jni.h>
#include <stdio.h>
  
JNIEXPORT jintArray JNICALL Java_fibonacci_returnfibo
(JNIEnv *env,jobject jobj,jint n)
{
  jintArray fiboarray  = (*env)->NewIntArray(env,n);
  
  int first=0;
  int second=1;
  int next;
  int i;
  int fibo[n];
  
  for(i=0;i<n;i++)
  {
        if(i<=1)
          next = i;
        else
        {
          next = first + second;
          first = second;
          second = next;
        }
  
        fibo[i] = next;
  }
  
  (*env)->SetIntArrayRegion(env,fiboarray,0,n,fibo);
  
return fiboarray;
}

 

将字符串从C传递到JAVA

示例:写了一个显示几个 Java 程序,这个星期是从 C 中的本机传递过来的。

Java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//daysofweek.java
  
public class daysofweek
{
  native String[] returndays();
  
   static
   {
      System.loadLibrary("daysofweek");
   }
  
   static public void main(String args[])
   {
  
      daysofweek obj = new daysofweek();
      String[] days = obj.returndays();
  
      System.out.println(" > The days of the week are :: ");
      for(String name: days)
        System.out.println(name);
   }
}

C代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//daysofweek.c
  
#include <jni.h>
#include <stdio.h>
  
JNIEXPORT jobjectArray JNICALL Java_daysofweek_returndays(JNIEnv *env, jobject jobj)
{
  
  char *days[]={"Sunday",
                "Monday",
                "Tuesday",
                "Wednesday",
                "Thursday",
                "Friday",
                "Saturday"};
  
  jstring str;
  jobjectArray day = 0;
  jsize len = 7;
  int i;
  
  day = (*env)->NewObjectArray(env,len,(*env)->FindClass(env,"java/lang/String"),0);
    
  for(i=0;i<7;i++)
  {
    str = (*env)->NewStringUTF(env,days[i]);
    (*env)->SetObjectArrayElement(env,day,i,str);
  }
    
return day;
}

如您所见,C 字符隐含(C 字符串)一个数组,而返回 Java 字符串类型代码。需要您不必担心 Java,最好的旧转换来救援🙂

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多