JNI小结

1.Introduction  
  JNI 是 Java Native Interface 的缩写,用以解决 Java 对本地方法的操作问题,而本地方法是以库文件的形式存放的(.dll for windows,.so for Unix)。  
   
2.JNICoding steps
1) 编写带有native声明的方法的java类
2) 使用javac命令编译所编写的java类
3) 使用javah java类名生成扩展名为h的头文件
4) 使用C/C++实现本地方法
5) 将C/C++编写的文件生成动态连接库

3. Examples
  
1) 编写java程序
   classHelloWorld 
   {
      /* 对将要调用的方法做本地声明,关键字为native。并且只需要声明,而不需要具体实现。*/    
      public native void sayHello(String aa);  

      /* 在类中声明所调用的库名称(这个库就是在下面 step 4 中对本地方法编译链接得到的动态库)
      * 在这里,库的扩展名字可以不用写出来,究竟是DLL还是SO,由系统自己判断。 
      */ 
     static 
      {
       System.loadLibrary("hello");
     }
  
      public static void main(String[] args) 
      {
         obj = newHelloWorld();
          obj.sayHello("Hello^-^");     
     }
   }
    
2) 编译 
   javac HelloWorld.java,生成HelloWorld.class;  

3) 生成头文件
   javah HelloWorld,生成扩展名为h的头文件。
[Note]:
如果你的java class是在某个package,比如 xxx 中,这里必须在 xxx 目录的上一层使用“javah xxx.HelloWorld”,则在xxx目录的上一层会生成一个名为xxx_HelloWorld.h 的头文件。
  
   其中,头文件的内容:

   /* DO NOT EDIT THIS FILE - it is machinegenerated */
   #include <jni.h>
   /* Header for class HelloWorld */
  
   #ifndef _Included_HelloWorld
   #define _Included_HelloWorld
   #ifdef __cplusplus
   extern "C" {
   #endif
   /*
    * Class:   HelloWorld
    * Method:  displayHelloWorld
    * Signature: ()V
    */
   JNIEXPORT void JNICALL Java_HelloWorld_sayHello
   (JNIEnv *, jobject, jstring);
  
   #ifdef __cplusplus
   }
   #endif
   #endif

[Note]:
a.  JNIEXPORT 和 JNICALL 都是 JNI 的关键字,表示此函数是要被 JNI 调用的。
b.  jstring 是以 JNI 为中介使Java的 String 类型与本地的 char* 沟通的一种类型。
c.  函数的名称是 JAVA_ 再加上 java 程序的 package 路径再加函数名组成的。
d.  参数中,我们也只需要关心在 Java 程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。

  
4) 编写本地方法
  
  实现和由 javah 命令生成的头文件里面声明的方法名相同的方法,然后编译连接成库文件,再把库文件拷贝到JAVA程序的路径下面即可。
 
   HelloWorldImp.c
  1 #include <jni.h>
  2 #include "HelloWorld.h"
  3 #include <stdio.h>
  4 JNIEXPORT jstring JNICALLJava_HelloWorld_sayHello
   (JNIEnv * env, jobject obj,  jstring s)
   {
       char * str;
      str=(char*)(*env)->GetStringUTFChars(env,s, NULL);
       printf("%s\n",str);
       (*env)->ReleaseStringUTFChars(env, s,str);
    }

[Note]:
a. 代码2中的第1行,需要将jni.h(该文件可以在%JAVA_HOME%/include文件夹下面找到)文件引入,因为在程序中的JNIEnv、jobject等类型都是在该头文件中定义的;
b. 在第2行需要将 HelloWorld.h 头文件引入。
  
5) 生成动态库  
  这里以在Windows中为例,需要生成dll文件。在保存HelloWorldImpl.c文件夹下面,使用VC的编译器cl成。
  
  cl -I%java_home%\include -I%java_home%\include\win32-LD HelloWorldImp.c -Fehello.dll
  
[Note]:
   生成的dll文件名在选项-Fe后面配置,这里是hello,因为在HelloWorld.java文件中我们loadLibary的时候使用的名字是hello。当然这里修改之后那里也需要修改。另外需要将-I%java_home%\include -I%java_home%\include\win32参数加上,因为在第四步里面编写本地方法的时候引入了jni.h文件。
  
6) 运行程序
  
  java HelloWorld 就ok。

 

4. JNI 语法

以 JNI 调用 本地 C 语言为例,在JNI中,既可使用 c 函数 (直接使用),又可使用 JAVA 函数。

在JNI里使用java类的某些方法,分为以下几个步骤:
1. 查找该类:
  jclass xxx = (*env)->FindClass(env, "Lclass_name;");

2. 查找该类的初始化方法:
  jmethodID xxx = (*env)->GetMethodID(env, jclass,"<init>", "(M)N");

3. 查找需要调用的该类的方法:
  jmethodID xxx = (*env)->GetMethodID(env, jclass, "(M)N" );

4. 初始化该类的实例:  
  jobject xxx = (*env)->NewObject(env, jclass, jmethodID );

5. 调用实例的某方法: 
  (*env)->CallObjectMethod(env, jclass, jmethodID,[parameter1, parameter2,...] );

6. 释放实例: 
   (*env)->DeleteLocalRef(env, xxx);

[Note]:
"(M)N"是该函数的输入和输出参数的类型签名(Type Signature)。 这里,JNI中对于以下类型定义了类型签名:
TypeSignature          Java Type 
Z                           boolean 
B                           byte 
C                           char 
S                          short 
I                          int 
J                           long 
F                           float 
D                           double 
L fully-qualified-class;   fully-qualified-class 
[type                     type[] 

e.g. 某函数输入double型,输出boolean型,则“(M)N”是"(D)Z",
如果输入是int型,输出是java的Object型,则“(M)N”是 "(I)Ljava/lang/Object;"
这里需注意,对于fully-qualified-class类型前面要加"L",后面加";"

e.g.
ArrayList.add : "(Ljava/lang/Object;)Z"
ArrayList.get:  "(I)Ljava/lang/Object;"
ArrayList_size: "()I"


对于 String 类型的实例化方法比较特殊, 1~4 步可以用调用 JNI 提供的实例化方法NewStringUTF/NewString 取代。下面是访问String的一些方法:
◆ GetStringUTFChars
      将jstring转换成为UTF-8格式的char*
◆ GetStringChars         将jstring转换成为Unicode格式的char*
◆ ReleaseStringUTFChars 释放指向UTF-8格式的char*的指针
◆ ReleaseStringChars    释放指向Unicode格式的char*的指针
◆ NewStringUTF           用 char* 创建一个UTF-8格式的 jstring 对象
◆ NewString             用char*创建一个Unicode 格式的 jstring对象
◆ GetStringUTFLengt     获取UTF-8格式的char*的长度
◆ GetStringLength       获取Unicode格式的char*的长度

e.g. 
jstring aaa =(*env)->NewStringUTF(env, "bbb");
char* bbb = (*env)->GetStringUTFChars(env, aaa, NULL);

e.g: 在JNI里使用java类的实例调用该类的一些方法HashMap.init(), put(), 以及ArrayList.size(),...etc.

JNIEnv *env;
jclass  clsHASHMAP;
jmethodID  ArrayList_add, ArrayList_get, ArrayList_size, HashMap_put,HashMap_init;
jstring key, value;
jobject objHashMap;

/* get class. */
clsHASHMAP = (*env)->FindClass(env,"Ljava/util/HashMap;");
/* get init method for HashMap */
HashMap_init =  (*env)->GetMethodID(env,clsHASHMAP,"<init>", "()V");
/* Call HashMap.init() to get a hashmap object */
objHashMap = (*env)->NewObject(env, clsHASHMAP, HashMap_init); 

HashMap_put = (*env)->GetMethodID(env, *clsHASHMAP,"put","(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
key = (*env)->NewStringUTF(env, "xxx");
Value = (*env)->NewStringUTF(env, "xxx");
/* put key/value to hashmap. */
(*env)->CallObjectMethod(env, objHashMap, HashMap_put, key, value);

/* 内存回收 */
(*env)->DeleteLocalRef(env, key);
(*env)->DeleteLocalRef(env, value);
(*env)->DeleteLocalRef(env, objHashMap);

 

e.g. /* get init method forInteger */
jclass clsINT;
jmethodID Integer_init;
jobject objINT;
clsINT = (*env)->FindClass(env,"Ljava/lang/Integer;");
Integer_init = (*env)->GetMethodID(env, clsINT, "<init>","(I)V")
objINT(*env)->NewObject(env,clsINT, Integer_init);

 

[Note]:
Solaris 上使用 JNI 调用JAVA某方法时,当返回值为 int 型时,返回值就会是个随意的假值。这可能是JNI的一个Bug。也就是说,在我们上面的例子中, ArrayList.size()方法尽量不要使用,不然在 Solaris 平台上会有问题。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值