• JNI 参数传递


      写例程之前先介绍一下代码目录结构吧,以免后面发生找不到library库的路径

                 

       so文件需要与java目录的根目录同级

    error1:Error: Could not find or load main class com.clay.example.sample1

    error2Exception in thread "main" java.lang.UnsatisfiedLinkError: no sample1 in java.library.path

          at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
          at java.lang.Runtime.loadLibrary0(Runtime.java:870)
          at java.lang.System.loadLibrary(System.java:1122)
          at com.clay.example.sample1.<clinit>(sample1.java:6)

    例程编写与编译请看下面的例子吧,代码结构目录需要严格对照上面的目录(当然你有自己的习惯也可以按照自己的习惯来,能编译通过都行),按照个人喜好来,不过新手的话,建议保持与上诉目录相同,不然会出现自己都不知道为啥的错误。

    Project目录下的 stringJNI是下例(传递字符串)的工程目录名称

    com/clay/example 是java 目录

    package com.clay.example 是java程序的包名

    cpp目录,我们javah 编译生成 .h 文件目录以及实现 .h 方法的 点C或者 点 cpp文件

    lib目录,我们生成的动态库目录,个人喜欢这样将其分开,保持代码目录的整洁性

    1、传递字符串

      本例程将由Java程序向C程序传入一个字符串,C程序对该字符串转成大写形式后回传给Java程序。

    Java源程序如下。

     1 package com.clay.example;
     2 
     3 public class sample1 {
     4     
     5     static {
     6         System.loadLibrary("sample1");
     7     }
     8 
     9     public native String stringMethod(String text);
    10 
    11     public static void main(String[] args) {
    12         sample1 sample = new sample1();
    13         String text = sample.stringMethod("I love Java");
    14 
    15         System.out.println("stringMethod: " + text);
    16     }
    17 }

      sample1.java以“I love Java”为参数调用libsample1.so中的函数stringMethod(),在得到返回的字符串后打印输出。

      写完之后,在 com/clay/example 目录下 执行命令:javac sample1.java 编译我们写的 java 程序,生成 sample.class 文件

      zhengchuanyu@CLAY:~/Projects/stringJNI/src$ javah -d cpp/ -classpath . -jni -v com.clay.example.sample1

      [Creating file RegularFileObject[cpp/com_clay_example_sample1.h]]

      切记:javah -d cpp/ -classpath . -jni -v com.clay.example.sample1 此命令生成的 jni 文件的头文件必须 java 程序的根目录执行,我们这里的根目录在 src 目录下

      此命令相关参数 -d <path> -classpath <path> -jni -v,请看上一篇博文,其中有详细的介绍

      生成的 .h 文件如下所示

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

      下面 c 代码的实现:jni_string.c

     1 #include <jni.h>
     2 #include "com_clay_example_sample1.h"
     3 #include <stdio.h>
     4 #include <string.h>
     5 #include <ctype.h>
     6 
     7 JNIEXPORT jstring JNICALL JNICALL Java_com_clay_example_sample1_stringMethod
     8 (JNIEnv *env, jobject obj, jstring string)
     9 {
    10     int i = 0;
    11 
    12     const char* str = (*env)->GetStringUTFChars(env, string, 0);
    13     char cap[128];
    14 
    15     strcpy(cap, str);
    16     (*env)->ReleaseStringUTFChars(env, string, str);
    17 
    18     for(i = 0; i < strlen(cap); i ++)
    19     {
    20         *(cap + i) = (char)toupper(*(cap + i));
    21     }
    22 
    23     return (*env)->NewStringUTF(env, cap);
    24 }

      首先请注意函数头部分,函数接收一个jstring 类 型的输入参数,并输出一个jstring类型的参数。jstring 是 jni.h 中定义的数据类型,是JNI框架内特有的字符串类型。

      程序的第 12 行是从 JNI 调用上下文中获取 UTF 编码的输入字符,将其放在指针 str 所指向的一段内存中。

      第 16 行是释放这段内存。

      第 23 行是将经过大写转换的字符串予以返回,这一句使用 NewStringUTF() 函数,将 C 语言的字符串指针转换为 JNI 的 jstring 类型。 JNIEnv 也是在 jni.h 中定义的,代表 JNI 调用的上下文。 GetStringUTFChars()、ReleaseStringUTFChars() 和 NewStringUTF() 均是 JNIEnv 的函数。

      后面会专门写一篇有关 JNI 相关函数的博文,前期只做例程学习参考,无需理解。

      将 jni_string.c 编译生成动态库

      命令: ~/Projects/stringJNI/src$ gcc -shared -fPIC -I /opt/jdk1.8.0_221/include/ -I /opt/jdk1.8.0_221/include/linux/ cpp/jni_string.c -o lib/libsample1.so

      运行程序,在 src 目录下(Java 程序所在的根目录)执行 :~/Projects/stringJNI/src$ java -Djava.library.path=lib/ com.clay.example.sample1

      -Djava.library.path=<path> 是指定动态库的路径

      否则会出现 上诉的 error1:Error: Could not find or load main class com.clay.example.sample1

      至于为什么会出现这些错误,只有自己多实践就知道为什么了,不要问我为什么知道,我也不知道,一开始目录我也是乱写,经过经验摸索总结而出,这其实就是 JNI 编写规范的一部分。

      运行结果如下:

                 

       输出:stringMethod: I LOVE JAVA

      后面的例程中,我将只列出代码目录结构,不再详细描述编译,本篇第一小节加上两篇博文有专门描述编译以及编写规则

     2、传递整型数组

      代码目录结构:

               

       本节例程将首次尝试在JNI框架内启用数组:C 程序向 Java 程序返回一个定长的整型数组成的数组,Java程序将该数组打印输出。

      Java程序的源代码如下。

     1 package com.clay.example;
     2 
     3 public class Sample2 {
     4     
     5     static {
     6         System.loadLibrary("Sample2");
     7     }
     8     
     9 
    10     public native int[] intArrayMethod();
    11 
    12     public static void main(String[] args) {
    13 
    14         Sample2 sample = new Sample2();
    15         int[] nums = sample.intArrayMethod();
    16 
    17         for(int i = 0; i < nums.length; i ++) {
    18             System.out.println("intArrayMethod: " + nums[i]);
    19         }
    20     }
    21 }

     jni_intArrayMethodImpl.c

     1 #include <jni.h>
     2 #include "com_clay_example_Sample2.h"
     3 #include <stdio.h>
     4 
     5 JNIEXPORT jintArray JNICALL Java_com_clay_example_Sample2_intArrayMethod
     6 (JNIEnv *env, jobject obj)
     7 {
     8     int i = 0;
     9 
    10     jintArray  array;//定义数组对象
    11 
    12     array = (*env)-> NewIntArray(env, 10);
    13 
    14     for(i = 0; i < 10; i++)
    15     {
    16         (*env)->SetIntArrayRegion(env, array, i, 1, &i);
    17     }                                
    18 
    19     /* 获取数组对象的元素个数 */
    20 
    21     int len = (*env)->GetArrayLength(env, array);
    22     
    23     /* 获取数组中的所有元素 */
    24     jint* elems = (*env)-> GetIntArrayElements(env, array, 0);                                               
    25 
    26     for( i = 0; i < len; i++)
    27     {
    28         printf("jni_intArrayMethodImpl: elems[%d] = %d
    ", i, elems[i]);
    29     }                                                
    30 
    31     return array;
    32 }

      jni_intArrayMethodImpl.c 涉及了两个jni.h定义的整型数相关的数据类型:jint和jintArray,jint是在JNI框架内特有的整数类型。程序的第 12 行开辟出一个长度为10 的jint数组,然后依次向该数组中放入元素0-9。21-24行不是程序的必须部分,纯粹是为了演示GetArrayLength() 和GetIntArrayElements()这两个函数的使用方法,前者是获取数组长度,后者则是获取数组的首地址以便于遍历数组。

    3、传递字符串数组

      未完待续

  • 相关阅读:
    浅谈flume
    浅谈storm
    浅谈zookeeper
    IntelliJ IDEA 使用教程
    浅谈spark
    添加本地jar包到maven仓库
    eclipse通过maven进行打编译
    pom.xml中添加远程仓库
    maven编译错误maven-assembly-plugin:2.2-beta-5:assembly (default-cli) on project
    最长上升子序列
  • 原文地址:https://www.cnblogs.com/Reverse-xiaoyu/p/13605529.html
Copyright © 2020-2023  润新知