java代码通过JNI调用c函数
一. JNI的基本原理及步骤
1. 总览
在Java代码中通过JNI调用C函数的步骤如下:
第一步:编写java代码 :在Java类中声明本地方法
第二步:编译java代码 :javac HelloJNI.java 生成 HelloJNI.class
第三步:生成C语言头文件 :使用javah命令,生成饱含JNI本地函数原型的头文件
第四步:编写C代码 :实现JNI本地函数
第五步:生成共享库 :生成C共享库
第六步:运行java程序 :通过JNI,调用JNI本地函数
2. 步骤
第一步:编写java代码:
class HelloJNI
{
/* 本地方法声明 */
native void printHello();
native void printString();
/* 加载库 libhellojni.so*/
static { System.loadLibrary("hellojni"); }
public static void main(String args[])
{
HelloJNI myJNI = new HelloJNI();
/* 调用本地方法(实际调用的是使用C语言编写的JNI本地函数) */
myJNI.printHello();
myJNI.printString("Hello World from printString fun");
}
}
第二步:编译java代码:使用
使用Java编译器(javac),编译第一步编写的Java源码。
#javac HelloJNI.java
生成HelloJNI.class。
第三步:生成C语言头文件
使用javah命令生成C函数原型的HelloJNI.h头文件。
#javah <包含native关键字声明的方法的Java类名称>
#javah HelloJNI
生成HelloJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */
#ifndef _Include_HelloJNI
#define _Include_HelloJNI
#ifdef __cplusplus
extern "c"{
#endif
/*
*Class : HelloJNI
*Method : printHello
*Signature: ()V
*/
JNIEXPORT void JNICALL java_HelloJNI_printHello(JNIEnv *,jobject);
/*
*Class : HelloJNI
*Method : printString
*Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL java_HelloJNI_printString(JNIEnv *,jobject,jstring);
#ifdef __cplusplus
}
#endif
#endif
第四步:编写C代码
在C函数原型生成后,开始编写hellojni.c文件,具体实现JNI本地函数。
首先把定义在HelloJNI.h头文件中的函数原型复制到hellojni.c文件中。
hellojni.c文件:
#include "HelloJNI.h"
#include <stdio.h>
/* 添加名称为 env 与 obj 的两个参数 */
JNIEXPORT void JNICALL java_HelloJNI_printHello(JNIEnv *,jobject)
{
printf("Hello World!\n");
return;
}
JNIEXPORT void JNICALL java_HelloJNI_printString(JNIEnv *,jobject,jstring)
{
/* 将java String转换为C字符串 */
const char *str = (*env)->GetStringUTFChars(env, string, 0);
printf("%s!\n",str);
return;
}
第五步:生成C共享库
经过第三步和第四步,C语言头文件与函数实现都已经完成,接下来,生成C共享库。
arm-linux-gcc -fPIC -shared hellojni.c -o hellojni.so
第六步:运行java程序
这样我们第一步中的java程序就可以加载hellojni.so文件,实现java调用C函数。
#java HelloJNI
Hello world!
Hello World from printString fun()!
3. 小结
(1)在Java类中声明本地方法
(2)使用javah命令,生成饱含JNI本地函数原型的头文件
(3)实现JNI本地函数
(4)生成C共享库
(5)通过JNI,调用JNI本地函数
二. 直接注册JNI本地函数
1.编写代码
在代码hellojni.c的基础上,经过添加、修改某些代码,并调用RegisterNatives()函数,将本地方法与C函数映射到一起,代替java虚拟机映射,最终代码hellojnimap.cpp文件如下:
/* 1.jni.h头文件包含各种JNI数据结构、JNI函数、宏定义等 */
#include "jni.h"
#include <stdio.h>
/* 2.JNI本地函数原型 */
void printHelloNative (JNIEnv *env,jobject obj);
void printStringNative(JNIEnv *env,jobject obj,jstring string);
JNIEXPORT jint JNICALL JIN_Onload(javaVM *vm,void *reserved)
{
JNIEnv* env = NULL;
JNINativeMethod nm[2];
jclass cls;
jint result = -1;
/* 3.判断java虚拟机是否支持JNI 1.4 */
if(vm->GetEnv((void**)&env,JNI_VERSION_1_4) != JNI_OK){
print("Error");
return JNI_ERR;
}
/*
4.此行代码用来将java类的本地函数与HelloJNI类的本地方法链接到一起,此行先调用FindClass()函数加载 HelloJNI类,并将类引用保存到jclass变量cls中。
*/
cls = env->FindClass(HelloJNI);
/* 5.该部分代码用来java类的本地方法与JNI本地函数映射到一起。结构体数组JNINativeMethod。 */
nm[0].name = "printHello";
nm[0].signature = "()V";
nm[0].fnPtr = (void*)printHelloNative;
nm[1].name = "printString";
nm[1].signature = "(Ljava/lang/String;)V";
nm[1].fnPtr = (void*)printStringNative;
}
void printHelloNative(JNIEnv *,jobject)
{
printf("Hello World!\n");
return;
}
void printStringNative(JNIEnv *,jobject,jstring)
{
/* 将java String转换为C字符串。 */
const char *str = (*env)->GetStringUTFChars(env, string, 0);
printf("%s!\n",str);
return;
}
2.编译生成动态库so
arm-linux-g++ -fPIC -shared hellojni.c -o hellojni.so