Qt on Android Episode 5 (translation), androidepisode

Source: Internet
Author: User

Qt on Android Episode 5 (translation), androidepisode

Address: http://www.kdab.com/qt-android-episode-5/

We have learned how to build the Qt on Android development environment, how to use Qt on Android, the available deployment policies, and how to sign applications. In this article, we will talk about JNI. (BogDan, I have been waiting for you for a long time. I didn't wait until I wrote "Qt on Android core programming ......)

Why JNI?

It is unrealistic for Qt to implement all Android functions. To use the functions available in the Android system, you must use JNI to access them. JNI is the only way to call each other between Java and C ++.

JNI Introduction

In this article, we will learn the basic knowledge of JNI. In the next article, we will study how to use the JNI knowledge introduced in this article to expand our Qt application.

There are too many discussions about JNI on the Internet. This article will focus on how to use JNI through Qt on Android. Starting from 5.2, Qt carries the Qt Android Extras module. When we have to use JNI, it gives us a more comfortable experience.

We will discuss two things:

  • Call a Java function from C ++
  • Calls back a C ++ function from Java.

Call a Java function from C ++

Using Qt Android Extras to call a Java function is quite simple.

First, create a static Java method:

// java file android/src/com/kdab/training/MyJavaClass.javapackage com.kdab.training; public class MyJavaClass{    // this method will be called from C/C++    public static int fibonacci(int n)    {        if (n < 2)            return n;        return fibonacci(n-1) + fibonacci(n-2);    }}

As you can see, the MyJavaClass class in the com. kdab. training Package defines FibonacciThis static method calculates the number of fibonacci and returns the result.

Now let's take a look at how to call the fibonacci method from Qt.

Step 1Because we need to use Qt Android Extras, we need to modify the. pro file as follows:

# Changes to your .pro file# ....QT += androidextras# ....

Step 2, Execute the call:

// C++ code#include <QAndroidJniObject>int fibonacci(int n){    return QAndroidJniObject::callStaticMethod<jint>                        ("com/kdab/training/MyJavaClass" // class name                        , "fibonacci" // method name                        , "(I)I" // signature                        , n);}

Yes! This is everything.

Let's take a closer look at this code and see what is in it:

  • We use QAndroidJniObject: callStaticMethod to call a Java method.
  • The first parameter is the full path class name, the package name is followed by a class name, And the. In the package name is replaced/
  • The second parameter is the method name.
  • The third parameter is the method signature.
  • The last parameter is the parameter we want to pass to the Java method.

Read the QAndroidJniObject document to learn details about the method signature and parameter type.

Calls back a C ++ function from Java.

To call back the C ++ method from Java, follow these steps:

  • Use the native keyword in Java to declare the native METHOD
  • Register THE native method in c ++
  • Call the native method in Java

Use the native keyword to declare the native METHOD

Let's slightly change the previous Java code:

// java file android/src/com/kdab/training/MyJavaClass.javapackage com.kdab.training; class MyJavaNatives{    // declare the native method    public static native void sendFibonaciResult(int n);} public class MyJavaClass{    // this method will be called from C/C++    public static int fibonacci(int n)    {        if (n < 2)            return n;        return fibonacci(n-1) + fibonacci(n-2);    }     // the second method that will be called from C/C++    public static void compute_fibonacci(int n)    {        // callback the native method with the computed result.        MyJavaNatives.sendFibonaciResult(fibonacci(n));    }}

Let's take a closer look at what is in this Code:

  • I personally prefer to isolate all native methods into an independent class, so I defined the MyJavaNatives class in the com. kdab. training Package and declaredSendFibonaciResultThis native method. StaticCompute_fibonacciMethod callSendFibonaciResultCall C ++ back and forth to send the calculation result, instead of passingFibonacci.
  • SendFibonaciResult, this method will be called by C ++ code, but it is notFibonacciReturns the calculation result directly.SendFibonaciResultThis native method calls the C ++ world back and forth to pass the computing results.

If you try to run the current code, it will fail. BecauseSendFibonaciResultNot yet registered. JVM does not know that it is a magic horse.

Use Java_Fully_Qualified_ClassName_MethodName to register a function

Code:

#include <jni.h>#include <QDebug> #ifdef __cplusplusextern "C" {#endif JNIEXPORT void JNICALL  Java_com_kdab_training_MyJavaNatives_sendFibonaciResult(JNIEnv *env,                                                    jobject obj,                                                    jint n){    qDebug() << "Computed fibonacci is:" << n;} #ifdef __cplusplus}#endif

Let's take a closer look and see what's amazing about this Code:

  • The first thing we see is that all functions must be exported as C functions, rather than C ++ functions.
  • The function name must follow the following template: Java key, package name, class name, method name, separated by a short underline
  • When the JVM loads the so file, it will scan this template and automatically register all the functions that match this template for you.
  • The first parameter env is a pointer of the JNIEnv type, pointing to a JNIEnv object.
  • The second parameter obj represents the Java object that you declare the native method.
  • For each function you want to register, the first and second parameters are mandatory and required.
  • Starting from the third parameter, it corresponds to the native method parameters declared in the Java class. Therefore, in our C ++ code, the third parameter corresponds to the first parameter of sendFibonaciResult in Java code.

It is very easy to use this method to register and declare the native method, but it has several disadvantages:

  • The function name is very long, for example, Java_com_kdab_training_MyJavaNatives_sendFibonaciResult. The ghost will remember it. Remember it, it's annoying to remember it.
  • Library must export all functions
  • The JVM cannot check the function signature because the function is exported in C instead of C ++.

Use JNIEnv: RegisterNatives to register native functions

To useJNIEnv: RegisterNativesTo register the native function, we need to do the following four steps:

The Code is as follows:

// C++ code#include <jni.h>#include <QDebug> // define our native methodstatic void fibonaciResult(JNIEnv */*env*/, jobject /*obj*/, jint n){    qDebug() << "Computed fibonacci is:" << n;} // step 2// create a vector with all our JNINativeMethod(s)static JNINativeMethod methods[] = {    { "sendFibonaciResult", // const char* function name;        "(I)V", // const char* function signature        (void *)fibonaciResult // function pointer     }}; // step 1// this method is called automatically by Java VM// after the .so file is loadedJNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/){    JNIEnv* env;    // get the JNIEnv pointer.    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)           != JNI_OK) {        return JNI_ERR;    }     // step 3    // search for Java class which declares the native methods    jclass javaClass = env->FindClass("com/kdab/training/MyJavaNatives");    if (!javaClass)        return JNI_ERR;     // step 4    // register our native methods    if (env->RegisterNatives(javaClass, methods,                            sizeof(methods) / sizeof(methods[0])) < 0) {        return JNI_ERR;    }    return JNI_VERSION_1_6;}

Let's check the code to better understand:

  • Static void maid (JNIEnv */* env */, jobject/* obj */, jint n)This is the method we registered and JVM will call it.
  • The first parameter env points to the JNIEnv object.
  • The second parameter obj is a reference to the Java object that declares the local method of fibonaciResult.
  • The first and second parameters are mandatory and required for the function to be exported.
  • Starting from the third parameter, it corresponds to the native method parameters declared in the Java class. Therefore, in our C ++ code, the third parameter corresponds to the first parameter of sendFibonaciResult in Java code.
  • We added this method to the JNINativeMethod method array.
  • The JNINativeMethod struct has the following members:
    • Const char * name-The function name must be the same as the native method name declared in Java.
    • Const char * signature-The function signature must be consistent with the native method parameter declared in Java.
    • Void * fnPtr-C ++ function pointer. In our code, it is the fibonaciResult. As you can see, the C ++ function name does not matter, because JVM only needs a pointer.
  • The remaining code is simple and clear, so there is no need to explain it again.

This method looks a little complicated, but it has the following benefits:

  • The name of the C ++ method can be yours.
  • Library only needs to export a function
  • Secure. JVM verifies the function signature.

Which method is used to register native functions is a matter of personal preference, but I recommend that you useJNIEnv: RegisterNativesThis method provides additional protection because it throws an exception when JVM detects that the function signature does not match.

Summary

In this article, we have learned the basic knowledge of JNI. In the next article, we will study how to use the JNI knowledge introduced in this article to expand our Qt application. We will discuss more about the architecture of the Qt on Android Application and how to expand the Java section of your application, we will also provide a practical example to illustrate how Qt threads and Java UI threads call each other.


Let's review the articles in the Qt on Android Episode series I have translated:

  • Qt on Android Episode 1)
  • Qt on Android Episode 2)
  • Qt on Android Episode 3)
  • Qt on Android Episode 4)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.