JNI-Linux

作者: LeftFlower | 来源:发表于2019-06-19 17:22 被阅读0次

JNI是Java Native Interface的缩写,Java源生接口/本地调用/本地接口,允许Java和其他语言代码进行互相调用,Java可以调用C/C++代码,C/C++也可以调用Java的。Java程序运行在JVM上,所以平台无关,但通过JNI调用原生代码C/C++就会丧失这个特性,破坏可移植性,至少原生代码部分总是需要重新编译的。但是一下情况是可以接受的。

1.使用一些旧的库

2.与硬件、操作系统进行交互

3.为了提高程序的性能

Note:jni.h就在%JAVA_HOME%\include下,还有%JAVA_HOME%\include\win32下的jni_md.h

Warning:javah在Java10,生成C/C++头文件的功能由javac -h代替

image

编写JAVA类

static代码段的System.load调用,这段代码表示在程序加载的时候,自动加载libHello.so库。
区分一下加载动态库的2种方式
一个概念是JVM有两个重要变量java.class.pathjava.library.path。前者指明了package的路径,后者指明了动态链接库的路径。
System.load();绝对路径
System.loadLibrary ();java.library.path下加载,不需要指明扩展名

public class Hello{

   public native static String sayHello(String name);

   static{
           System.load("/usr/project/test/libHello.so");
         #System.loadLibrary("Hello");
     }

    public static void main(String[] args){
             Hello hello = new Hello();
             String ret = hello.sayHello("Simona");
             System.out.println(ret);
   }
 }

编译JAVA类

javac Hello.java

生成Hello.h

javah -jni Hello
or
javac -h . Hello
Note:JAVA 10弃用了javah

有包名的情况下需要指定classpath,不然会出现UnsatisfiledLinkError

javah -classpath 'your package path' -jni 'package'.Hello
or
指明共享库路径
java -Djava.library.path=. Hello
export LD_LIBRARY_PATH=“Hello路径”:$LD_LIBRARY_PATH

编写Native Method

生成的Hello.h的头文件带有需要实现的Native Method,实现这个Native Method需要指定参数名称
cat Hello.h
vim hello.c
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Hello */

#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Hello
 * Method:    sayHello
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_Hello_sayHello
  (JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif
#include <stdio.h>
#include "Hello.h"

JNIEXPORT jstring JNICALL Java_Hello_sayHello(JNIEnv *env, jclass jc, jstring name)
{
    const char *buf;

    buf = (*env)->GetStringUTFChars(env, name, NULL);
    if (NULL == buf)
    {
        return NULL;
    }

    printf("%s\n", buf);
    
    (*env)->ReleaseStringUTFChars(env, name, buf);

    return (*env)->NewStringUTF(env, "hello");
}

GetStringUTFChars
返回指向字符串的 UTF-8 字符数组的指针,如果不能创建这个字符数组,返回 null。该数组在被ReleaseStringUTFChars()释放前将一直有效
const char* GetStringUTFChars(jstring string,jboolean* isCopy)

NewStringUTF
利用UTF-8字符数组构造新java.lang.String对象
(*env)->NewStringUTF(env, "hello");

制作动态库

由于是Linux平台,需要制作后缀是.so的动态库,其中,需要指定jni.h的路径,必要时还需要jni_md.h的路径,该文件在jdk的目录中。这里指定了jni.h和jni_md.h的路径。
一个库的必须要是:lib+库名+.so。链接的时候只需要提供库名就可以了

find / -name jni.h
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.221-2.6.18.0.el7_6.x86_64/include/jni.h
find / -name jni_md.h
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.221-2.6.18.0.el7_6.x86_64/include/linux/jni_md.h

gcc -c -fPIC -I/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.221-2.6.18.0.el7_6.x86_64/include/ -I/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.221-2.6.18.0.el7_6.x86_64/include/linux/ Hello.c -o Hello.o

gcc -shared Hello.o -o libHello.so

调用动态库

java Hello
Simona
hello

出现UnsatisfiedLinkError

pwd
/usr/project/test

export LD_LIBRARY_PATH=/usr/project/test:$LD_LIBRARY_PATH
java Hello
Simona
hello

or

java -Djava.library.path=. Hello
Simona
hello

创建调用类

import java.util.*;
public class HelloJni
{
     public static void main(String argv[])
     {
         System.out.println( System.getProperty("java.library.path"));
         new HelloJni();
     }

     public HelloJni()
     {
         new Hello().sayHello("Hello,Jni !"); //调用Hello的原生函数sayHello
     }
}

执行调用

javac HelloJni.java
java HelloJni
/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
Hello,Jni !

参考网址

JNI编程实现(Linux)

相关文章

  • JNI-Linux

    JNI是Java Native Interface的缩写,Java源生接口/本地调用/本地接口,允许Java和其他...

网友评论

      本文标题:JNI-Linux

      本文链接:https://www.haomeiwen.com/subject/xblhqctx.html