Part 3
启动Qt程序
通过Java启动Qt程序可以调用命令行, 这样Qt会在另一个进程开始.
|
1
2
3
4
5
6
7
8
9
10
|
public static void launchSampleApp()
{ Runtime
rn = Runtime.getRuntime(); Process
p = null; try { String
command = "QtAppSample"; p
= rn.exec(command); } catch (Exception
e) { System.out.println("JAVA
Failed to launch Sample."); } } |
>用进程启动Qt可能在通信效率和资源共享方面有些影响.
Qt事件循环是个dead loop, 如果直接在JNI中启动Qt程序会把Java的主线程Block住; Qt main event loop will block the Java main thread;
Java 启动Qt需要另起一个线程
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class Main{ public static JNISample
sample = new JNISample(); public static void main(String[]
args) { Thread
t = new Thread(new Runnable()
{ public void run()
{ sample.launchSample(); } }); t.start(); }} |
>JNISample的launchSample()函数是一个native方法
|
1
|
public native void launchSample(); |
C++方面, 可以使用static instance的方式来引用Qt类;
Qt class: 类似singleton, 可以在JNI的cpp函数实现中引用静态的Qt的类来启动Qt程序;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class QML_EXPORT
QMLSample : public QObject{ Q_OBJECTpublic: static QMLSample
* GetInstance();private: QMLSample
();private: QDeclarativeView*
mpView; JNIEnv*
mpEnv; static QMLSample
* mpSInstance;}; |
JNI函数启动Qt程序
|
1
2
3
4
5
6
7
8
9
10
11
|
JNIEXPORT void JNICALL
Java_JNISample_launchSample (JNIEnv
*env, jobject obj){ Q_UNUSED(obj); int argc
= 0; char**
argv = NULL; QApplication
app(argc, argv); QMLSample::GetInstance()->Show(); QMLSample::GetInstance()->SetJNIEnv(env); app.exec();} |
跨线程通信
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)
|
1
2
3
4
5
6
7
|
const QMetaObject*
metaObj = QMLSample::GetInstance()->metaObject();int methodIndex
= metaObj->indexOfMethod("FunctionName(int,QString)");QMetaMethod
method = metaObj->method(methodIndex);bool ret
= method.invoke(QMLDLLSample::GetInstance(), Qt::AutoConnection, Q_ARG(int,
i), Q_ARG(QString,
string)); |
>这样就能跨线程调用Qt动态库的函数;
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中启动Qt程序,避免阻塞Java主线程,并探讨了使用signal/slot进行跨线程通信的方法,以及QMetaObject::invoke()在异步调用中的应用和注意事项。
1618

被折叠的 条评论
为什么被折叠?



