zoukankan      html  css  js  c++  java
  • 在Linux 平台下使用 JNI

    在Linux 平台下使用 JNI

     

     

    简介: 本文简要介绍了 JNI调用规范,及常用函数。并通过具体示例程序展示了实现一个本地调用的基本步骤。

    本文的标签:  best_practicesjni应用开发

     

     

     

     

    发布日期: 2002 年 10 月 29日 
    级别: 初级 
    访问情况 : 8136次浏览 
    评论: 0 (查看 | 添加评论 -登录)

    平均分 3 星 共 25 个评分 平均分(25个评分)
    为本文评分

     

    引言

    Java的出现给大家开发带来的极大的方便。但是,如果我们有大量原有的经过广泛测试的非Java 代码,将它们全部用 Java来重写,恐怕会带来巨大的工作量和长期的测试;如果我们的应用中需要访问到特定的设备,甚至是仅符合公司内部信息交互规范的设备,或某个特定的操作系统才有的特性,Java就显得有些力不从心了。面对这些问题,Sun 公司在JDK1.0 中就定义了 JNI 规范,它规定了 Java应用程序对本地方法的调用规则。

     

    回页首

    实现步骤及相关函数使用

    本文将一步步说明在 Linux平台下如何实现本地共享库与 Java 协同工作。HelloWorld程序是目前标准的入门第一步,那么,我也以类似的应用最为样例。

    第一步,定义一个 Java 类 -- Hello. 它提供SayHello 方法:

    此时应注意两点:

    1.为要使用的每个本地方法编写本地方法声明,其声明方式与普通Java 方法接口没什么不同,只是必须指定 native关键字,如下所示:

    publicnative void SayHello(String strName);

    在这个函数中,我们将根据传进的人名,向某人问好。

    2.必须显式地加载本地代码库。我们需在类的一个静态块中加载这个库:

     static      {      System.loadLibrary("hello");      }   
    

    再加上必要的异常处理就生成如下源文件Hello.java:

     public class Hello   {    static    {     try     {   // 此处即为本地方法所在链接库名     System.loadLibrary("hello");     }     catch(UnsatisfiedLinkError e)     {      System.err.println( "Cannot load hello library:
     " +                                  e.toString() );     }    }    public Hello()    {    }   // 声明的本地方法    public native void SayHello(String strName);   }  
    

    编译后生成 Hello.class 文件。

    第二步,生成本地链接库。具体过程如下:

    1. 要为以上定义的类生成 Java本地接口头文件,需使用 javah,Java 编译器的 javah功能将根据 Hello 类生成必要的声明,此命令将生成Hello.h 文件,我们在共享库的代码中要包含它,javah不使默认内部命令,需要指明路径,它在 JDK 的 bin目录下,在我的 Linux 环境下命令如下:

    /home/jbuilder/jdk1.3.1/bin/javah Hello

    生成的 Hello.h 文件 内容如下所示:

        #include <jni.h>      #ifndef _Included_Hello   #define _Included_Hello   #ifdef __cplusplus   extern "C" {   #endif      JNIEXPORT void JNICALL Java_Hello_SayHello    (JNIEnv *, jobject, jstring);   #ifdef __cplusplus   }   #endif   #endif  
    

    2. 在与 Hello.h 相同的路径下创建一个 CPP 文件Hello.cpp。内容如下:

     #include "Hello.h"  #include <stdio.h>   // 与 Hello.h 中函数声明相同  JNIEXPORT void JNICALL Java_Hello_SayHello  (JNIEnv * env, jobject arg, jstring instring)   {     // 从 instring 字符串取得指向字符串 UTF 编 的指针  const jbyte *str =          (const jbyte *)env->GetStringUTFChars( instring, JNI_FALSE );      printf("Hello,%s
    ",str);    // 通知虚拟机本地代 不再需要通过 str 访问 Java 字符串。     env->ReleaseStringUTFChars( instring, (const char *)str );      return;   }  
    

    所有的 JNI 调用都使用了 JNIEnv *类型的指针,习惯上在 CPP文件中将这个变量定义为evn,它是任意一个本地方法的第一个参数。env指针指向一个函数指针表,在 VC中可以直接用"->"操作符访问其中的函数。

    jobject 指向在此 Java 代码中实例化的 Java 对象LocalFunction 的一个句柄,相当于 this 指针。

    后续的参数就是本地调用中有 Java程序传进的参数,本例中只有一个 String 型参数。对于字符串型参数,因为在本地代码中不能直接读取Java 字符串,而必须将其转换为 C /C++ 字符串或Unicode。以下是三个我们经常会用到的字符串类型处理的函数:

    const char*GetStringUTFChars(jstring string,jboolean* isCopy)

    返回指向字符串 UTF编码的指针,如果不能创建这个字符数组,返回null。这个指针在调用 ReleaseStringUTFChar()函数之前一直有效。

    参数:string    Java 字符串对象  isCopy   如果进行拷贝,指向以 JNI_TRUE 填充的 jboolean, 否则指向以 JNI_FALSE 填充的 jboolean。  void ReleaseStringUTFChars(jstring str, const char* chars)  通知虚拟机本地代 不再需要通过 chars 访问 Java 字符串。
    

    参数:string    Java 字符串对象  chars   由 GetStringChars 返回的指针  jstring NewStringUTF(const char *utf)  返回一个新的 Java 字符串并将 utf 内容拷贝入新串,如果不能创建字符串对象, 返回 null。通常在反值类型为 string 型时用到。
    

    参数:utf    UTF 编 的字符串指针 对于数值型参数,在 C/C++ 中可直接使用,其字节宽度如下所示:
    
    Java C/C++ 字节数
    boolean jboolean 1
    byte jbyte 1
    char jchar 2
    short jshort 2
    int jint 4
    long jlong 8
    float jfloat 4
    double jdouble 8

    对于数组型参数,

    Java C/C++
    boolean[ ] JbooleanArray
    byte[ ] JbyteArray
    char[ ] JcharArray
    short[ ] JshortArray
    int[ ] JintArray
    long[ ] JlongArray
    float[ ] JfloatArray
    double[ ] JdoubleArray

    对于上述类型数组,有一组函数与其对应。以下函数中Xxx 为对应类型。 
    xxx * GetXxxArrayElements(xxxArray array, jboolean*isCopy) 
    产生一个指向 Java 数组元素的 C指针。不再需要时,需将此指针传给ReleaseXxxArrayElemes。

    参数:array    数组对象  isCopy   如果进行拷贝,指向以 JNI_TRUE 填充的 jboolean, 否则指向以 JNI_FALSE 填充的 jboolean。 例如: jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy)   void ReleaseXxxArrayElements(xxxArray array,xxx *elems, jint mode)  通知虚拟机不再需要从 GetXxxArrayElements 得到的指针。
    
    参数:array    数组对象  elems   不再需要的指向数组元 的指针  mode    0 =在更新数组元 后释放 elems 缓冲器  JNI_COMMIT =在更新数组元 后不释放 elems 缓冲器  JNI_ABORT =不更新数组元 释放 elems 缓冲器 例如:void ReleaseBooleanArrayElements(jbooleanArray array,jboolean *elems,     jint mode)   xxxArray NewXxxArray(jsize len)  产生一个新的数组,通常在反值类型为数组型时用到
    
    参数:len   数组中元 的个数。 例如:jbooleanArray NewBooleanArray(jsize len)
    

    3 .编译生成共享库。

    使用 GCC 时 , 必须通知编译器在何处查找此 Java本地方法的支持文件,并且显式通知编译器生成位置无关的代码,在我的环境中按如下过程编译:

    gcc -I/home/jbuilder/jdk1.3.1/include      -I/home/jbuilder/jdk1.3.1/include/linux -fPIC -c Hello.c 
    

    生成 Hello.o

     gcc -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 Hello.o  
    

    生成 libhello.so.1.0

    接下来将生成的共享库拷贝为标准文件名

    cplibhello.so.1.0 libhello.so

    最后通知动态链接程序此共享文件的路径。

    exportLD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH

    4 .编写一个简单的 Java程序来测试我们的本地方法。

    将如下源码存为 ToSay.java:

     
  • 相关阅读:
    LintCode "Maximum Gap"
    LintCode "Wood Cut"
    LintCode "Expression Evaluation"
    LintCode "Find Peak Element II"
    LintCode "Remove Node in Binary Search Tree"
    LintCode "Delete Digits"
    LintCode "Binary Representation"
    LeetCode "Game of Life"
    LintCode "Coins in a Line"
    LintCode "Word Break"
  • 原文地址:https://www.cnblogs.com/leeeee/p/7276709.html
Copyright © 2011-2022 走看看