JNI和Qt通信 (Part 1)

Part1

Java Native Interface-JNI-JAVA本地调用

JNI标准是Java平台的一部分, 允许Java代码和其他语言进行交互;

开始实现->

Step 1) 编写Java代码, 编写一个JNI接口HelloJNI.java

1
2
3
4
5
6
7
8
9
10
11
public  class  HelloJNI {
    static  {
       System.loadLibrary( "hello" );  // hello.dll (Windows) or libhello.so (Unixes)
    }
    // A native method that receives nothing and returns void
    private  native  void  sayHello();
 
    public  static  void  main(String[] args) {
       new  HelloJNI().sayHello();   // invoke the native method
    }
}

sayHello()是一个native方法, 这个方法会在生成的JNI header文件中声明C/C++的函数; 

loadLibrary()会在当前路径(实际上是Java Library Path)下寻找并加载名为hello的动态库, 所有的dependency都会在当前路径下加载; 
对于不同的平台loadLibrary()会自动搜索不同的后缀名; e.g. Sample.dll(Windows), libSample.so(Linux);
你也可以指定路径: "-Djava.library.path=path_to_lib", 路径错误的话会有"UnsatisfiedLinkError";

相应还有load()函数, 需要指定路径和dependency的路径; 
dlltool http://sourceware.org/binutils/docs/binutils/dlltool.html

Note 动态库加载必须在static块内, 保证首先进行;


Step 2) 编译和生成C/C++ header

1
javac HelloJNI.java

编译Java生成class;

1
javah HelloJNI

javah命令会生成相应的header: HelloJNI.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */
 
#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern  "C"  {
#endif
/*
  * Class:     HelloJNI
  * Method:    sayHello
  * Signature: ()V
  */
JNIEXPORT  void  JNICALL Java_HelloJNI_sayHello
   (JNIEnv *, jobject);
 
#ifdef __cplusplus
}
#endif
#endif

在相应的cpp文件中实现函数: JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject);

名字转换的格式: Java_{package_and_classname}_{function_name}(JNI arguments)

-JNIEnv*参数: 指向JNI的环境, 给你调用JNI函数的权限;

-jobject参数: 指向Java的"this"对象;

-extern "C"会被C++编译器识别, 把函数用C的命名方式来编译.


Step 3) 编译C/C++库

HelloJN.cpp的实现:

1
2
3
4
5
6
#include <stdio.h>
#include "HelloJNI.h"
JNIEXPORT  void  JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
    printf ( "Hello World!\n" );
    return ;
}

>jni.h的路径一般是在<JAVA_HOME>\include" 和 "<JAVA_HOME>\include\win32";

Note 不同环境下的编译选项是不同的;

Windows下的gcc必须加上一个参数 "-Wl,--add-stdcall-alias -shared"; 
VC++的cl和Linux下的gcc不需要这个参数;

1
gcc -Wl,--add-stdcall- alias  -I "<JAVA_HOME>\include"  -I "<JAVA_HOME>\include\win32"  -shared -o hello.dll HelloJNI.cpp

>"-Wl"会把选项"--add-stdcall-alias"传输给链接器, 防止"UnsatisifiedLinkError".(一般导出的名字有"@nn"的前缀, 这个选项会把导出的名字加上没有前缀的别名)
有些时候也会使用 "-Wl,--kill-at".

>"-I"指定JNI头文件路径, 路径有空格时加上双引号.

可以使用nm命令列出函数导出的外部符号: 

1
nm hello.dll | grep  say

>windows: "nm -g file.dll"


Step 4) 运行JNI程序

1
java -Djava.library.path=. HelloJNI

>"-Djava.library.path=<path_to_lib>" 是可选的, 作为虚拟机的选项来制定动态库的路径.

Linux下可能需要设置路径:

1
export  LD_LIBRARY_PATH=.

设置library路径为当前目录, 或者将so放入java执行目录下;


Other

编译链接相关

alias name that without @

"gcc -Wl,--add-stdcall-alias -I"C:\Program Files (x86)\Java\jdk1.7.0_17\include" -I"C:\Program Files (x86)\Java\jdk1.7.0_17\include\win32" -shared -o HelloWorld.dll HelloWorld.c"
"cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloWorldImp.c -Fehello.dll"

Compile time and Link Time: -L, -I, -Wl,rpath=<your_lib_dir>

Linux: LD_LIBRARY_PATH; ldd; ldconfig; nm; readlf; Id; 

<refer to> http://blog.csdn.net/unbutun/article/details/6362474 & http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html


JNI header

classpath: should point to the root folder where your top level package (JNI) goes to, not to the folder where your class is physically located.

http://stackoverflow.com/questions/14795050/javah-command-for-native-methods-gives-exception

1) "javah HelloWorld" (all the config is set)

2) "javah -o "JNIDemo.h" -jni -classpath "R:\Projects\JAVA\JavaJNIDemo\build\classes" javajnidemo.JavaJNIDemo"

1
javah -o  "<HeaderPATH>\JNIHeader.h"  -jni -classpath  "JavaClassPath"  JNISample


JNI在Package里的case

1
package myjni;  //...Java codes

>JNI的类会被放入"myjni"的package内, 文件保存为"myjni\HelloJNI.java"

编译: 加上package(路径)名

1
javac myjni\HelloJNI.java

javah: 使用package名, 把头文件生成到include文件夹下.

1
javah -d include myini.HelloJNI

头文件的native函数: Java_<fully-qualified-name>_methodName

1
JNIEXPORT  void  JNICALL Java_myjni_HelloJNI_sayHello(JNIEnv *, jobject);

---End---


你可能感兴趣的:(JNI和Qt通信 (Part 1))