JNI和Qt通信 (Part 3)

来源:互联网 发布:单片机软件调试 编辑:程序博客网 时间:2024/04/30 18:23

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_OBJECT
 
public:
    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---

原创粉丝点击