Qt on Android episode 5 (translation)

Source: Internet
Author: User

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

We already know how to build a QT on Android development environment, how to use Qt on Android, what deployment strategies are available and how to sign the app, and it's time to move on. In this article, we talk about JNI. (BogDan, I waited for you for a long time, when I wrote "Qt on Android core programming " did not wait for ... )

Why JNI is required

Because Qt is not realistic to realize all the features of Android. To use the features that are already available on the Android system, you need to access them through JNI. JNI is the only way to invoke each other between Java and C + +.

Introduction to JNI

In this article we are going to learn the basics of JNI, and in the next article we will look at how to use the JNI knowledge presented in this article to extend our Qt application.

There's too much discussion about JNI on the web, and this article will focus on how to use JNI with Qt on an Android system. From 5.2 onwards, QT has been carrying the QT Android Extras module. When we have to use JNI, it provides us with a more comfortable experience.

We will discuss two things:

    • Calling a Java function from C + +
    • Callback a C + + function from Java

Calling a Java function from C + +

Using Qt Android Extras to invoke a Java function is fairly straightforward.

First, let's create a static method of Java:

java file Android/src/com/kdab/training/myjavaclass.javapackage com.kdab.training;  public class myjavaclass{    //The This method would be called from C + + public    static int Fibonacci (int n)    {        if (N < 2)            return n;        Return Fibonacci (n-1) + Fibonacci (n-2);    }}

As you can see, we have defined within the Myjavaclass class within the com.kdab.training package FibonacciThis static method calculates the number of Fibonacci and returns the result.

Now let's take a look at how to call Fibonacci this method from Qt.

The first step , because we want to use Qt Android Extras, so we need to modify the. Pro file, as follows:

# changes to your. Pro file# .... QT + = androidextras# ....

Step Two, 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! That's all the stuff.

Let's take a closer look at the code and see what's Inside:

    • We use Qandroidjniobject::callstaticmethod to invoke a Java method
    • The first parameter is the full path class name, followed by a class name, and the package name. To be replaced by/
    • The second parameter is the method name
    • The third parameter is a method signature
    • The last parameter is the parameter we want to pass to the Java method

Read the Qandroidjniobject documentation for details on method signatures and parameter types.

Callback a C + + function from Java

In order to callback the C + + method from Java, you can do this by following these steps:

    • Use the native keyword in Java to declare the native method
    • Registering the native method in C + +
    • Call the native method in Java

Declaring the native method with the native keyword

Let's change the previous Java code a little bit:

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); SS myjavaclass{    //This method would be called from C + + public    static int Fibonacci (int n)    {        if (N < 2 )            return n;        Return Fibonacci (n-1) + Fibonacci (n-2);    }     The second method, that would be, called from C + + public    static void Compute_fibonacci (int n)    {        //Callba CK the native method with the computed result.        Myjavanatives.sendfibonaciresult (Fibonacci (n));}    }

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

    • I personally prefer to isolate all native methods into a separate class, so I define the Myjavanatives class within the com.kdab.training package, declaring Sendfibonaciresult this native Method. The static Compute_fibonacci method calls Sendfibonaciresult to and fro C + +, sending the result of the calculation instead of doing it through the Fibonacci return value.
    • Sendfibonaciresult , this method is called by the C + + code, but it does not return the result of the calculation directly as Fibonacci , it uses sendfibonaciresult Native method to callback the C + + world to pass the calculation results.

If you try to run the current code, it will fail. Because Sendfibonaciresult is not registered, the JVM has no idea that it is a god horse thing.

Using the Java_fully_qualified_classname_methodname registration 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 at the magic of this code:

    • The first thing we see is that all functions must be exported as C functions, not C + + functions
    • The function name must follow the following template: Java key, package name, class name, method name, split with short underscore
    • When the JVM loads so files, it scans the template and automatically registers all of your functions that match this template.
    • The first parameter, env, is a pointer to the JNIENV type, pointing to a JNIEnv object
    • The second parameter, obj, represents the Java object for which you declared the native method.
    • For each function you want to register, the first and second arguments are mandatory and must be
    • Starting from the third parameter, corresponding to the parameters of the native method declared in the Java class, one after the other. So, in our C + + code, the third parameter corresponds to the first parameter of Sendfibonaciresult in the Java code.

Registering and declaring the native method in this way is simple, but it has several drawbacks:

    • The function of the name of the giant, such as Java_com_kdab_training_myjavanatives_sendfibonaciresult, I remember to live, remember to knock is also annoying not
    • The library must export all functions
    • Insecure, the JVM has no way to check the signature of the function because the function is exported in C, not C + +

Use Jnienv::registernatives to register the native function

In order to use jnienv::registernatives to register the native function, we need to do the following four steps:

    1. In the first step, we need to access the jnienv pointer. The simplest method is to define and export the Jni_onload method, and each so file can be defined in any CPP file at a time.
    2. The second step is to create an array for the C + + method we want to export
    3. The third step is to use Jnienv::findclass to find the ID of the Java class that declares these native methods
    4. Fourth step, call Jnienv::registernatives (java_class_id, Methods_vector, N_methods)

The code is as follows:

C + + code#include <jni.h> #include <QDebug>//define our native methodstatic void Fibonaciresult (jnienv */*e nv*/, 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 was 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, size    Of (methods)/sizeof (Methods[0])) < 0) {return jni_err; } return jni_version_1_6;}

Let's check the code to better understand:

    • static void Fibonaciresult (JNIEnv */*env*/, Jobject/*obj*/, Jint N), this is the method that we registered, and the JVM will invoke it.
    • The first parameter, env, points to a JNIEnv object
    • The second parameter, obj, is a reference to the Java object that declares Fibonaciresult this local method
    • The first and second parameters are mandatory and necessary for the function to be exported
    • Starting from the third parameter, corresponding to the parameters of the native method declared in the Java class, one after the other. So, in our C + + code, the third parameter corresponds to the first parameter of Sendfibonaciresult in the Java code.
    • We've added this method to the Jninativemethod type of method array.
    • The JNINATIVEMETHOD structure has the following members:
      • Const char* name-the function name must match the name of the native method declared in Java
      • Const char* Signature -function signature, must be consistent with the parameters of the native method declared in Java
      • void* fnptr -C + + function pointers, in our code, is Fibonaciresult. As you can see, the C + + function name does not matter, because the JVM only needs a pointer.
    • The rest of the code is simple and clear, there is no need to explain the pro, there are comments, ha.

This approach is a little bit more complicated to use, but it has the following benefits:

    • The name of the C + + method can be followed by your own
    • Library only needs to export a function
    • Security, the JVM verifies the function signature

Which way to register the native function is a matter of personal preference, but I recommend the use of jnienv::registernatives because it provides additional protection: An exception is thrown when the JVM detects that the function signature does not match.

Summarize

In this article we have learned the basics of JNI, and in the next article we will look at how to use the JNI knowledge presented in this article to extend our Qt application. We'll talk more about the architecture of Qt on Android apps, how to extend the Java portion of your app, and we'll provide a practical example of how to call each other between QT threads and Java UI threads.


Review my translation of Qt on Android episode series articles:

    • Qt on Android episode 1 (translation)
    • Qt on Android episode 2 (translation)
    • Qt on Android episode 3 (translation)
    • Qt on Android episode 4 (translation)

Qt on Android episode 5 (translation)

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.