java代码通过JNI调用c函数

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
发布了10 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_40083589/article/details/103765908
今日推荐