lance的专栏

专注移动互联网开发,千里之行-始于足下

Java jni 动态库 so

简单JNI的使用--在Java中调用C库函数

特别注意在windows下执行vc++的cl命令需要添加两处环境变量
%主目录%\Microsoft Visual Studio\Common\MSDev98\Bin
%主目录%\Microsoft Visual Studio\VC98\Bin
在Android Framework中,需要提供一种媒介或桥梁,将Java层(上层)与C/C++(底层)有机地联系起来,使得它们相互协调,
共同完成某些任务。在这两层之间充当连接桥梁这一角色的就是Java本地接口(JNI,Java Native Interface),
它允许Java代码与基于C/C++编写的应用和库进行交互操作。
JNI提供了一系列接口,允许Java类与使用C/C++等其它编程语言(在JNI中,这些语言被称为本地语言)编写的应用程序、模块、
库进行交互操作。比如,在Java类中使用C语言库中中的特定函数,或在C语言里面使用Java类库,都需要借助JNI来完成。
通常会在下列几种情况下使用JNI
 注重处理速度:如果对某段程序的执行速度有较高的要求,建议使用C/C++编写代码,而后在Java层通过JNI调用基于C/C++编写的部分代码。
 硬件控制:为了更好地控制硬件,硬件控制代码通常使用C语言编写,借助JNI将其与Java层连接起来,从而实现对硬件的控制。
 已有C/C++代码的复用:在编写程序的过程中,常常会使用已经编写好的C/C++代码,既提高了编程效率,又确保了程序的安全性和健壮性。
 在复用这些C/C++代码时,就要通过JNI来实现。
在Java代码中通过JNI调用C函数的步骤如下:
 第一步:编写Java代码
 第二步:编译Java代码(javac Java文件)
 第三步:生成C代码头文件(javah java类名,自动生成)
 第四步:编写C代码(实现C代码头文件里面的函数)
 第五步:生成C共享库(使用工具编译生成C共享库,win下面为dll文件,Linux下面为so文件)
 第六步:运行Java程序(java 类名)
第一步:编写Java代码
       首先编写调用C语言的Java源代码HelloJNI.java
 public class HelloJNI {
          native void printHello();                      1
          native void printString(String str);
       static{
            System.loadLibrary("hellojni");        2
        }
          public static void main(String[] args) {
             HelloJNI myJNI = new HelloJNI();
               myJNI.printHello();
             myJNI.printString("Hello world form printString function!");
          }
}
说明:
     1在Java类中,使用”native”关键字,声明本地方法,该方法与用C/C++编写的JNI本地函数相对应。
     ”native”关键字告知Java编译器,在Java代码中带有该关键字的方法只是声明,具体由C/C++等其它语言编写实现。
       2在Java类中声明了本地方法之后,接下来,调用System.loadLibrary()方法,加载具体实现本地方法的C运行库
       (在Java中加载本地运行库通常使用静态块(static block))。System.loadLibrary()
       方法加载由字符串参数指定的本地库,在不同操作系统平台下,加载的C运行库不同。在Window下面,调用
       System.loadLibrary(“hellojni”),则hellojni.dll会被加载;在Linux下面,
       则会加载libhellojni.so文件。
第二步:编译Java代码
       使用如下命令编译java源代码:
              javac HelloJNI.java
       编译好HelloJNI.java后,生成HelloJNI.class文件。如果此时直接运行java程序,就是抛出异常。
       由于尚未创建加载到Java代码中的hellojni.dll库文件,无法找到Java虚拟机要加载的C运行库。
       接下来,创建hellojni.dll库文件。
第三步:生成C代码头文件
       若想创建本地方法的映射C函数,必须先生成函数原型,函数原型存在于C/C++头文件中。Java提供了javah工具,位于JAVA
       JDK的安装目录的bin目录下面,用来生成包含函数原型的C/C++头文件,使用方法如下:
       javah <包含以native关键字声明方法的Java类名称>
       运行javah命令,会在当前目录下生成与Java类名(即javah命令的参数)相同名称的C语言头文件。在生成的C头文件中,
       定义了与Java本地方法相链接的C函数原型。
       以下为生成的HelloJNI.h文件内容:
/* DO NOT EDIT THIS FILE - it is machine generated */          1
#include <jni.h>
/* Header for class HelloJNI */
#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloJNI
 * Method:    printHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloJNI_printHello  
  (JNIEnv *, jobject);
/*
 * Class:     HelloJNI                                                                          2
 * Method:    printString
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_HelloJNI_printString
  (JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
说明:
       1该文件由javah命令生成,为了保证JNI正常运行,请不要直接修改本文件的内容,JNI开发者只要使用C/C++语言实现定义
       的函数即可。
       2这是javah命令生成的两个C函数原型,函数原型在Java类中声明的本地方法的基础上生成。查看各函数原型注释,可以看到与
       各函数原型对应的Java代码中的本地方法,注释中标明了三个元素:类名、本地方法、本地方法签名。     
       接下来分析一下函数原型:JNIEXPORT、JNICALL都是JNI关键字,表示此函数要被JNI调用,函数原型中必须有这两个关键字,
       JNI才能正常调用函数。其实,JNIEXPORT、JNICALL两个关键字都是宏定义,
       在JDK_HOME/include/win32/jni_md.h文件(在Window平台下)中。
       观察函数原型名称,可以发现函数名称遵循一定的命名规则:JNI支持的函数命名形式为”Java_类名_本地方法名”。
       通过函数命名即可推断出JNI本地函数与哪个Java类的哪个本地方法相对应。
       在生成的函数原型中,带有两个默认参数,分别为JNIEnv * 与jobject,支持JNI的函数必须包含这两个共同参数。
       第一个参数JNIEnv *为JNI接口指针,用来调用JNI表中的各种JNI函数(这里的JNI函数是指JNI中提供的基本函数集);
       第二个参数jobject也是JNI提供的Java本地方法,用来在C代码中访问Java对象,此参数中保存着调用本地方法的对象的一个引用。
       在JNI编程中,Java程序与C/C++函数间经常进行数据交换,如果不提供一种方法消除两种语言的数据类型的差异,
       那么程序就无法正常运行,运行的可靠性也无法保障。JNI提供了一套与Java数据类型相对应的Java本地类型,
       使得本地语言可以使用Java数据类型,如下表所示:
Java类型
本地类型
字节(bit)
boolean-jboolean
8,
byte-jbyte
8
char-jchar
16,
short-jshort
16
int-jint
32
long-jlong
64
float-jfloat
32
double-jdouble
64
void-void
n/a
       以上Java本地类型定义在JDK_HOME/include/jni.h文件中。此外,Java本地类型也提供了另外三种类型,
       分别对应于Java类、对象与字符串三种引用类型数据(除此之外还有几种引用类型,有兴趣可以可以查阅相关资料)。
Java引用类型
Java本地类型

Jclass
对象
Jobject
String-Jstring
第四步:编写C代码
       在C函数原型生成后,开始编写hellojni.c文件,具体实现JNI本地函数。首先,把定义在HelloJNI.h头文件中的函数原型
       复制到hellojni.c,注意在使用javah命名生成的头文件中,函数的参数仅指定了参数的类型,并未给出参数的名称,
       因此复制完函数原型,开始实现C函数时,必须先在参数类型后指定具体的参数名称。
       以下为编写好的hellojni.c代码:
#include "HelloJNI.h"
#include <stdio.h>
//添加名称为env与obj的两个参数
JNIEXPORT void JNICALL Java_HelloJNI_printHello(JNIEnv *env, jobject obj)
{
       printf("Hello world!\n");
       return ;
}
JNIEXPORT void JNICALL Java_HelloJNI_printString(JNIEnv *env, jobject obj,
jstring string)
{
       //将Java String转换以C字符串
       const char * str = (*env)->GetStringUTFChars(env,string,0);
       printf("%s!\n",str);
       return ;
}
       GetStringUTFChars ()是JNI函数,用来将Java字符串转换成C语言字符串。JNI提供了多种JNI函数,
       用来处理C字符串与Java字符串的转换,具体可以参考:
第五步:生成C共享库
       在编写好了hellojni.c之后,使用编译器将其编译成hellojni.dll文件。这里使用的是
       Visual C++ 2008 Express Editions。安装好之后,使用Visual Studio 2008 命令提示,
       输入编译指令:
cl -I "F:\Java\jdk1.7.0\include" -I "F:\Java\jdk1.7.0\include\win32"
-LD hellojni.c -Fe hellojni.dll
执行结果为:
 结果会显示
 /dll
 /implib:hellojni.lib
 /out:hellojni.dll
 hellojni.obj
     正在创建库 hellojni.lib 和对象 hellojni.exp
指令说明:
       cl:visual c++编译命令
       -I<dir>:添加要检索头文件的目录路径<dir>
                 为了检索头文件,添加如下目录
                     jni.h(<JDK_HONE>\include)
                     jni_md.h(JDK_HONE>\include\win32)
       -LD:创建DLL
       -Fe<文件名>:指定编译结果文件名称
第六步:运行Java程序
       执行java指令,运行HelloJNI类。

  java HelloJNI


android技术上如有疑问可以问我,有问必答.

爱品茶的盆友,光顾小店(谢谢,能收藏最好了大笑):

http://lancezone.taobao.com


专注移动开发!继续前行~


阅读更多
文章标签: java jni so 动态库
个人分类: Android(原创)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭