java 利用jni 来调用本地系统文件操作

Java 本机接口(Java Native Interface (JNI))是一个本机编程接口,它是 Java 软件开发工具箱(Java Software Development Kit (SDK))的一部分。JNI 允许 Java 代码使用以其它语言(譬如 C 和 C++)编写的代码和代码库。Invocation API(JNI 的一部分)可以用来将 Java 虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用 Java 代码。


预备知识
所有示例都是使用 Java、C 和 C++ 代码编写的,并可以移植到 Windows 和基于 UNIX 的平台上。要完全理解这些示例,您必须有一些 Java 语言编程经验。此外,您还需要一些 C 或 C++ 编程经验。严格来说,JNI 解决方案可以分成 Java 编程任务和 C/C++ 编程任务,由不同的程序员完成每项任务。然而,要完全理解 JNI 是如何在两种编程环境中工作的,您必须能够理解 Java 和 C/C++ 代码。


系统需求
浏览器:Netscape 4.x 或更高版本, 或者 Internet Explorer 4.x 或更高版本 ,支持 JavaScript 。 要运行本教程中的示例,您需要下列工具与组件: Java 编译器:随 SDK 一起提供的 javac.exe。 Java 虚拟机(JVM):随 SDK 一起提供的 java.exe。 本机方法 C 文件生成器:随 SDK 一起提供的 javah.exe。 定义 JNI 的库文件和本机头文件。jni.h C 头文件、jvm.lib 和 jvm.dll 或 jvm.so 文件,这些文件都是随 SDK 一起提供的。 能够创建共享库的 C 和 C++ 编译器。最常见的两个 C 编译器是用于 Windows 的 Visual C++ 和用于基于 UNIX 系统的 cc。


解说:
一个手机的项目,需要JAVA程序在发送短信的时候和第三方的短信服务器连接。短信接口是用C++写的。需要用到JNI的主体部分。
  介绍一个简单的JNI的调用的过程。
  JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。
  JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。
  简单介绍及应用如下:
  一、JAVA中所需要做的工作
  在JAVA程序中,首先需要在类中声明所调用的库名称,如下:
  static {
  System.loadLibrary(“goodluck”);
  }

  在这里,库的扩展名字可以不用写出来,究竟是DLL还是SO,由系统自己判断。
  还需要对将要调用的方法做本地声明,关键字为native。并且只需要声明,而不需要具 体实现。如下:
  public native static void set(int i);
  public native static int get();
  然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C++的头文件。
  例如程序testdll.java,内容为:
  public class testdll
  {
  static
  {
  System.loadLibrary("goodluck");
  }
  public native static int get();
  public native static void set(int i);
  public static void main(String[] args)
  {
  testdll test = new testdll();
  test.set(10);
  System.out.println(test.get());
  }
  }

  用javac testdll.java编译它,会生成testdll.class。
  再用javah testdll,则会在当前目录下生成testdll.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。
  二、C/C++中所需要做的工作
  对于已生成的.h头文件,C/C++所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C++所实现的功能了。
  接上例子。我们先看一下testdll.h文件的内容:
  /* DO NOT EDIT THIS FILE - it is machine generated */
  #include
  /* Header for class testdll */
  #ifndef _Included_testdll
  #define _Included_testdll
  #ifdef __cplusplus
  extern "C" {
  #endif
  /*
  * Class: testdll
  * Method: get
  * Signature: ()I
  */
  JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass);
  /*
  * Class: testdll
  * Method: set
  * Signature: (I)V
  */
  JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);
  #ifdef __cplusplus
  }
  #endif
  #endif
  在具体实现的时候,我们只关心两个函数原型
  JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass); 和
  JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);
  这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVA的int类型与本地的int沟通的一种类型,我们可以视而不见,就当做int使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。
  好,下面我们用testdll.cpp文件具体实现这两个函数:
  #include "testdll.h"
  int i = 0;
  JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass)
  {
  return i;
  }
  JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint j)
  {
  i = j;
  }
  编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是goodluck.dll 。把goodluck.dll拷贝到testdll.class的目录下,java testdll运行它,就可以观察到结果了。
  我的项目比较复杂,需要调用动态链接库,这样在JNI传送参数到C程序时,需要对参数进行处理转换。才可以被C程序识别。
  大体程序如下:
  public class SendSMS {
  static
  {
  System.out.println(System.getProperty("java.library.path"));
  System.loadLibrary("sms");
  }
  public native static int SmsInit();
  public native static int SmsSend(byte[] mobileNo, byte[] smContent);
  }
  在这里要注意的是,path里一定要包含类库的路径,否则在程序运行时会抛出异常:
  java.lang.UnsatisfiedLinkError: no sms in java.library.path
  at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1491)
  at java.lang.Runtime.loadLibrary0(Runtime.java:788)
  at java.lang.System.loadLibrary(System.java:834)
  at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)
  at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
  Exception in thread "main"
  指引的路径应该到.dll文件的上一级,如果指到.dll,则会报:
  java.lang.UnsatisfiedLinkError: C:\sms.dll: Can't find dependent libraries
  at java.lang.ClassLoader$NativeLibrary.load(Native Method)
  at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1560)
  at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1485)
  at java.lang.Runtime.loadLibrary0(Runtime.java:788)
  at java.lang.System.loadLibrary(System.java:834)
  at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)
  at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
  Exception in thread "main"


  通过编译,生成com_mobilesoft_sms_mobilesoftinfo_SendSMS.h头文件。(建议使用Jbuilder进行编译,操作比较简单!)这个头文件就是Java和C之间的纽带。要特别注意的是方法中传递的参数jbyteArray,这在接下来的过程中会重点介绍。
  /* DO NOT EDIT THIS FILE - it is machine generated */
  #include
  /* Header for class com_mobilesoft_sms_mobilesoftinfo_SendSMS */
  #ifndef _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS
  #define _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS
  #ifdef __cplusplus
  extern "C" {
  #endif
  /*
  * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS
  * Method: SmsInit
  * Signature: ()I
  */
  JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit
  (JNIEnv *, jclass);
  /*
  * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS
  * Method: SmsSend
  * Signature: ([B[B)I
  */
  JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend
  (JNIEnv *, jclass, jbyteArray, jbyteArray);
  #ifdef __cplusplus
  }
  #endif
  #endif

  对于我要调用的C程序的动态链接库,C程序也要提供一个头文件,sms.h。这个文件将要调用的方法罗列了出来。
  /*
  * SMS API
  * Author: yippit
  * Date: 2004.6.8
  */
  #ifndef MCS_SMS_H
  #define MCS_SMS_H
  #define DLLEXPORT __declspec(dllexport)
  /*sms storage*/
  #define SMS_SIM 0
  #define SMS_MT 1
  /*sms states*/
  #define SMS_UNREAD 0
  #define SMS_READ 1
  /*sms type*/
  #define SMS_NOPARSE -1
  #define SMS_NORMAL 0
  #define SMS_FLASH 1
  #define SMS_MMSNOTI 2
  typedef struct tagSmsEntry {
  int index; /*index, start from 1*/
  int status; /*read, unread*/
  int type; /*-1-can't parser 0-normal, 1-flash, 2-mms*/
  int storage; /*SMS_SIM, SMS_MT*/
  char date[24];
  char number[32];
  char text[144];
  } SmsEntry;
  DLLEXPORT int SmsInit(void);
  DLLEXPORT int SmsSend(char *phonenum, char *content);
  DLLEXPORT int SmsSetSCA(char *sca);
  DLLEXPORT int SmsGetSCA(char *sca);
  DLLEXPORT int SmsSetInd(int ind);
  DLLEXPORT int SmsGetInd(void);
  DLLEXPORT int SmsGetInfo(int storage, int *max, int *used);
  DLLEXPORT int SmsSaveFlash(int flag);
  DLLEXPORT int SmsRead(SmsEntry *entry, int storage, int index);
  DLLEXPORT int SmsDelete(int storage, int index);
  DLLEXPORT int SmsModifyStatus(int storage, int index); /*unread -> read*/
  #endif

  在有了这两个头文件之后,就可以进行C程序的编写了。也就是实现对JNI调用的两个方法。在网上的资料中,由于调用的方法实现的都比较简单,(大多是打印字符串等)所以避开了JNI中最麻烦的部分,也是最关键的部分,参数的传递。由于Java和C的编码是不同的,所以传递的参数是要进行再处理,否则C程序是会对参数在编译过程中提出警告,例如;warning C4024: 'SmsSend' : different types for formal and actual parameter 2等。
  Sms.c的程序如下:
  #include "sms.h"
  #include "com_mobilesoft_sms_mobilesoftinfo_SendSMS.h"
  JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit(JNIEnv * env, jclass jobject)
  {
  return SmsInit();
  }

  JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend(JNIEnv * env, jclass jobject, jbyteArray mobileno, jbyteArray smscontent)
  {
  char * pSmscontent ;
  //jsize theArrayLengthJ = (*env)->GetArrayLength(env,mobileno);
  jbyte * arrayBody = (*env)->GetByteArrayElements(env,mobileno,0);
  char * pMobileNo = (char *)arrayBody;
  printf("[%s]\n ", pMobileNo);
  //jsize size = (*env)->GetArrayLength(env,smscontent);
  arrayBody = (*env)->GetByteArrayElements(env,smscontent,0);
  pSmscontent = (char *)arrayBody;
  ....


reference:
http://www.jcwcn.com/html/Java/10_43_37_489.htm
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java调用JNI实例的过程如下: 在Java调用JNI实例的第一步是通过System.loadLibrary()方法加载包含本地函数实现的动态链接库。这将使得Java程序能够找到并访问JNI实例中的方法。 接下来,在Java代码中声明一个native方法,这个方法将由JNI实例来实现。native关键字告诉编译器该方法是一个JNI方法。 然后,使用javah命令自动生成一个C/C++头文件,该头文件中包含了Java声明的native方法的函数原型。头文件的生成步骤是在命令行中进入Java源代码目录,并执行javah命令。 接下来,打开生成的头文件,将native方法的函数原型拷贝到一个C/C++源文件中,并实现该方法。 在C/C++源文件中,需要包含jni.h头文件,并实现native方法的功能。在实现JNI方法时,可以使用JNIEnv接口调用Java方法,通过JNI实例和Java对象进行交互。 最后,使用C/C++的编译器将源文件编译成动态链接库。 在Java代码中,可以通过调用声明的native方法来使用JNI实例,这样就可以调用C/C++中实现的功能了。 需要注意的是,Java和C/C++之间的数据类型转换可能需要注意,因为Java和C/C++使用不同的数据类型系统。为了正确传递和处理参数,可能需要使用JNIEnv接口的一些方法来处理数据类型的转换。 总结来说,Java调用JNI实例的过程包括加载JNI实例的动态链接库、声明native方法、使用javah命令自动生成头文件、实现native方法的功能、编译源文件成动态链接库、通过调用native方法来使用JNI实例。 ### 回答2: Java调用JNI实例是指在Java程序中使用JNI技术(Java Native Interface)来调用本地方法。 JNIJava与本地代码之间的桥梁,它允许Java程序在运行时调用本地编程语言(如C、C++)编写的代码。这样做的好处是可以结合Java的跨平台特性和本地编程语言的高效性能。 要在Java程序中调用JNI实例,首先需要编写本地代码。以C语言为例,我们需要编写一个与Java类对应的本地方法。 编写本地方法时,需要使用特定的修饰符来表明这是一个本地方法,并且使用JNI提供的函数来与Java交互。然后将本地代码编译成动态链接库(如.so文件)。 然后,在Java程序中通过System.loadLibrary方法加载动态链接库。加载成功后,就可以通过Java调用本地方法了。 在调用本地方法时,需要使用native关键字来声明这是一个本地方法,并且使用JNI提供的接口来调用本地方法。 在调用JNI实例时,需要注意以下几点: 1. 确保本地代码与Java代码的数据类型匹配,可以使用JNI提供的函数转换数据类型。 2. 传递参数时,可以将Java的对象、基本数据类型或数组作为参数传递给本地方法。 3. 在本地方法中修改Java的对象时,需要使用JNI提供的函数来获取并修改Java对象的属性或调用Java对象的方法。 总结: Java调用JNI实例是一种充分利用Java跨平台特性和本地编程语言高性能的方式。通过编写本地代码并加载动态链接库,可以在Java程序中调用本地方法。在调用JNI实例时,需要注意数据类型匹配和参数传递等问题。 ### 回答3: Java调用JNIJava Native Interface)是一种机制,用于在Java程序中调用本机(Native)方法。Java开发者可以使用JNI将具有特定目标平台相关的操作或功能集成到Java应用程序中。 为了实现Java调用JNI,开发者首先需要编写一个Java类,其中包含用于调用本机方法的声明。然后,在本机方法中编写原生代码,以实现需要的功能。在编写完本机代码后,需要将其编译为动态连接库(.dll,.so等),以供Java程序调用。 在Java类中,使用`native`关键字声明本机方法。然后,通过JavaJNI库提供的函数,将本机方法与本机库中对应的函数进行关联。具体步骤如下: 1. 编写Java类,并在其中声明本机方法的原型,使用`native`关键字标记这些方法。 2. 使用Java的`javac`命令编译Java类,生成字节码文件。 3. 使用Java的`javah`命令生成头文件,该头文件包含了本机方法的声明。 4. 在C/C++中编写实现本机方法的代码,并将其编译为动态连接库。可以根据平台的不同,使用不同的编译选项和工具链进行编译。 5. 将生成的动态连接库与Java程序绑定。这可以通过在Java程序中使用`System.loadLibrary("library_name")`的方式来实现,其中`library_name`为动态连接库的名称。 6. 在Java程序中调用本机方法,通过JNI库提供的函数进行连接。 需要注意的是,Java调用JNI的过程需要遵循特定的规范和约定,以确保本机方法与Java代码之间的正确交互。此外,使用JNI可能会涉及到与本机平台相关的问题,比如内存管理和线程安全等。因此,在使用JNI时,开发者需要仔细考虑潜在的问题,并进行适当的测试和调试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值