JNI加载Native Library 以及 跨线程和Qt通信
2013-10-24 11:56
441 查看
JNI加载Native Library 以及 跨线程和Qt通信
Part1Java Native Interface-JNI-JAVA本地调用
JNI标准是Java平台的一部分, 允许Java代码和其他语言进行交互;
开始实现->
Step 1) 编写Java代码, 编写一个JNI接口HelloJNI.java
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
名字转换的格式: Java_{package_and_classname}_{function_name}(JNI arguments)
-JNIEnv*参数: 指向JNI的环境, 给你调用JNI函数的权限;
-jobject参数: 指向Java的"this"对象;
-extern "C"会被C++编译器识别, 把函数用C的命名方式来编译.
Step 3) 编译C/C++库
HelloJN.cpp的实现:
Note 不同环境下的编译选项是不同的;
Windows下的gcc必须加上一个参数 "-Wl,--add-stdcall-alias -shared";
VC++的cl和Linux下的gcc不需要这个参数;
有些时候也会使用 "-Wl,--kill-at".
>"-I"指定JNI头文件路径, 路径有空格时加上双引号.
可以使用nm命令列出函数导出的外部符号:
Step 4) 运行JNI程序
Linux下可能需要设置路径:
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"
编译: 加上package(路径)名
Part2
JNI数据转换成C数据
e.g. jstring - GetStringUTFChars(), NewStringUTF(), ReleaseStringUTFChars()
JNI的数据定义
Read: http://stackoverflow.com/questions/819536/how-to-call-java-function-from-c
Windows http://public0821.iteye.com/blog/423941
Linux http://blog.sina.com.cn/s/blog_48eef8410100fjxr.html
JNI数据类型
Java Type | Native Type | Description |
boolean | jboolean | 8 bits, unsigned |
byte | jbyte | 8 bits, signed |
char | jchar | 16 bits, unsigned |
double | jdouble | 64 bits |
float | jfloat | 32 bits |
int | jint | 32 bits, signed |
long | jlong | 64 bits, signed |
short | jshort | 16 bits, signed |
void | void | N/A |
Java Type | Signature |
boolean | Z |
byte | B |
char | C |
double | D |
float | F |
int | I |
long | J |
void | V |
object | Lfully-qualified-class; |
type[] | [type |
method signature | ( arg-types) ret-type |
Java side
Java版本 "java -version"
反编译工具 javap:
JNI实现过程中的Issue
x86 or x64 "Can't load load IA 32-bit dll on a amd 64 bit platform"
确定本机上的默认JVM的版本和动态库的版本一致(x86或x64), Make sure JAVA's default path; check with "java -version" in command line.
3rdParty can't find dependent libraries 保证所依赖的动态库都能被找到;
1) copy the dll into executable file's folder 2) System.load() the dlls by dependecy orders
JNI_CreateJavaVM failed
C++创建JVM调用Java方法
http://docs.oracle.com/javase/1.4.2/docs/guide/jni/jni-12.html#JNI_CreateJavaVM & /article/10419329.html
[我机器上装了多个版本的Java, 测试的时候没有成功]
jvm.dll(C:\Program Files (x86)\Java\jdk1.7.0_17\jre\bin\client; C:\Program Files (x86)\Java\jdk1.7.0_17\jre\bin\server;
need check); jvm.lib(C:\Program Files (x86)\Java\jdk1.7.0_17\lib)
<Refer to> http://home.pacifier.com/~mmead/jni/cs510ajp/ & http://www.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html
Sample http://chnic.iteye.com/category/20179
JNI doc http://docs.oracle.com/javase/7/docs/technotes/guides/jni/
>JNA https://github.com/twall/jna/ XstartOnFirstThread
---End---
Part 3
启动Qt程序
通过Java启动Qt程序可以调用命令行, 这样Qt会在另一个进程开始.
Qt事件循环是个dead loop, 如果直接在JNI中启动Qt程序会把Java的主线程Block住; Qt main event loop will block the Java main thread;
Java 启动Qt需要另起一个线程
Qt class: 类似singleton, 可以在JNI的cpp函数实现中引用静态的Qt的类来启动Qt程序;
signal/slot
Java在子线程启动了Qt, 如果Java要向Qt发送消息的话, 需要使用signal/slot的方式.
Note 如果直接使用JNI调用Qt的directly方法, e.g. setWindowTitle(), Qt会报错: "setProperty
: Cannot send events to objects owned by a different thread"
除了 1)signal/slot, 还可以显式使用 2)QMetaObject::invoke(), 利用MetaObject机制调用Qt函数
Note 信号发送方式需要改为 Qt::QueuedConnection
(或者使用默认的AutoConnection)
e.g,2)
Note invoke的格式必须严格遵守, 多一个空格就错, must stictly follow the format, e.g.:metaObj->indexOfMethod("Function(int,QString)"),
no space is allowed between "int," and "QString".
对于MetaObject无法识别的类型: 使用qRegisterMetaType()来注册: "QMetaMethod::invoke:
Unable to handle unregistered datatype 'MyType'"
使用invoke异步调用函数的时候, 是无法得到return的返回值的: "It is unable to QMetaObject::invokeMethod with return values in queued connections"
Solution: 1) 把函数的参数改为指针, 来传递想要得到的值; ---由于是在异步的消息机制下, 这个也是不行的;
所以只能这样: 2) 得到值以后再发个消息....或者调用Java对象的方法传递值;
---End---
相关文章推荐
- JNI加载Native Library 以及 跨线程和Qt通信
- JNI加载Native Library 以及 跨线程和Qt通信
- vue.js移动端app之上拉加载以及下拉刷新实战
- Echars柱状图异步加载数据以及X轴纵向显示实例
- 线程返回值以及线程锁
- linux Apache设置https访问以及加载mod_ssl.so模块以及问题解决
- ASP.NET页面事件加载顺序以及Global.asax文件的使用
- 带图片的ListView(GridView)的图片异步加载、OOM处理以及图片和数据缓存策略的研究
- Android NDK开发简介 NDK和SDK以及JNI有什么关系?
- 区分Activity的四种加载模式----以及Intent的setFlags
- Java 使用线程方式Thread和Runnable,以及Thread与Runnable的区别
- Android中级第八讲--安卓子线程,以及定时任务使用讲解
- thinkphp框架加载流程以及原理
- 【转】java中三个类别加载器的关系以及各自加载的类的范围
- AsyncTask异步加载以及Proxy代理模式访问网络数据
- 从源码看DL4J中Native BLAS的加载,以及配置
- 网页铵钮动态加载用户控件以及其事件
- JavaWeb 服务启动时,在后台启动加载一个线程。
- 可延迟函数、内核微线程以及工作队列