Once Java Function Is Called

  • Download tutorial

Introduction

C++ and Java are two mainstream languages, with their strengths and weaknesses, and enough of interesting portable code around. So why not enjoy from the all-time of both worlds?

The Java Native Interface (JNI) is a standard to integrate in a portable way C++ and Java code. Information technology works in both directions: you tin can call a C++ library from Java or you can call Coffee components from C++. In this tutorial, I'll explain the 2d approach.

Background

JNI is frequently used by Java developers to telephone call some tiny portions of C++ code when ultra-high functioning is required. But JNI is more than merely that. Information technology also allows you to embed existing Java components into your C++ developed software.

In this article, yous'll larn how to utilise JNI, raw JNI and only JNI to accomplish such an integration. No third political party wrapper will be used.

In one case you've read this tutorial, y'all'll no longer regret that some cutting edge software components are offset developed for Coffee and only afterwards available for C++. Y'all'll no longer write circuitous file based or network based interface to link Java and C++ code when tight and time disquisitional interrelation is required. You'll seemlessly integrate both worlds.

Tutorial

  • Download tutorial

Prerequisites

For this tutorial, yous demand to have:

  • An installed Coffee Developer'southward Kit (JDK). Let's telephone call the installation directory <JDK-DIR>
  • An installed Java Runtime environment (JRE). Let'due south say the installation directory is <JRE-DIR>. Annotation that the JDK installation sets up a JRE automatically.
  • A working C++ toolchain. My explanation is tailored for MSVC13 on Windows. But equally the code is standard, the explanation could hands exist adapted for other compilers and operating systems.

You lot must add <JRE>/bin/server to the PATH. This is something you must do unless you are immune to copy the Java Virtual Car dynamic library (JVM.dll) into the path of your executable.

For convenience, yous should ensure that the JDK tools in <JDK-DIR>/bin are included in the PATH: you then can easily compile your Java code.

Project Setup

For each C++ projects in this tutorial, you lot must add the directories <JDK-DIR>/include and <JDK-DIR>/include/win32 to the include directories of your compiler. Annotation that the win32 directory Is platform dependent.

[SCREENSHOT]

You lot also shall add <JDK-DIR>/lib/jvm.lib to the linker input files equally additional dependencies).

With MSVC2103, you do this by right clicking on the project, to brandish its properties (meet screenshot).

The ZIP file contains an MSVC2013 solution with all the 7 examples of this tutorial. Download Article-JNI-one.nothing

Commencement Example: Load and Initialize the JVM

Before using JNI in your C++ code, you take to load and initialize the Java Virtual Car (JVM). The following code shows you how to exercise this:

          #include                      <            jni.h            >                    int          main() {        Using          namespace          std;        JavaVM *jvm;                             JNIEnv *env;                                        JavaVMInitArgs vm_args;                               JavaVMOption* options =          new          JavaVMOption[1];          options[0].optionString =          "          -Djava.form.path=.";          vm_args.version = JNI_VERSION_1_6;                    vm_args.nOptions =          1;                                 vm_args.options = options;        vm_args.ignoreUnrecognized =          false;                       jint rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);            delete          options;              if          (rc != JNI_OK) {                            cin.go();              get out(EXIT_FAILURE);        }                  cout          <          <          "          JVM load succeeded: Version ";        jint ver = env->GetVersion();        cout          <          <          ((ver>          >          xvi)&0x0f)          <          <          "          ."          <          <(ver&0x0f)          <          <          endl;                 jvm->DestroyJavaVM();        cin.get(); }

This code displays the version of the JVM. The instance 1 code is enriched with some error processing that should assistance you to solve any unexpected problems.

If y'all don't get an fault message, but your program gets interrupted abruptly, it'll most probably be that jvm.dll could non exist constitute in the path (see prerequisites above).

Case 2: Admission a Unproblematic Java Static Method

Allow'south write the simplest Java method nosotros could imagine to telephone call: a uncomplicated static method, taking no argument and returning nothing. In Java, everything is embedded in a class. So, we'll write the post-obit lawmaking in the file MyTest.java:

          public          class          MyTest {          private          static          int          magic_counter=777;          public          static          void          mymain() {             System.out.println("          Howdy, World in java from mymain");          System.out.println(magic_counter);      } }

We compile this lawmaking from the command line:

javac MyTest.coffee

We then cheque that in that location was no error and that the file MyTest.class was successfully generated. Past the fashion, I won't tell it but we'll proceed this way for all subsequent examples.

Now, nosotros are ready to enrich our previous C++ code:

... jclass cls2 = env->FindClass("          MyTest");          if(cls2 ==          nullptr) {     cerr          <          <          "          Mistake: class not found !"; }          else          {                                      cout          <          <          "          Class MyTest found"          <          <          endl;     jmethodID mid = env->GetStaticMethodID(cls2,          "          mymain",          "          ()5");            if(mid ==          nullptr)         cerr          <          <          "          ERROR: method void mymain() not found !"          <          <          endl;          else          {         env->CallStaticVoidMethod(cls2, mid);                              cout          <          <          endl;     } }        

How does this work?

  • Nosotros showtime have to find the right grade with FindClass(), which acts equally a class loader. It will search for an appropriate .class file in the listing of directories that was provided at JVM initialization. If the Java class is included in a package, you lot shall provide its full name.
  • The form is then passed to GetStaticMethod(), which shall detect the correct method in the class. The terminal parameter of this function is the most difficult one: the method signature. In instance of any mismatch hither, the method won't exist institute. "()" means a part with no parameter and "V" that the return blazon is void.
  • A static method is independent of any object. And then we can and so phone call the method with CallStaticVoidMethod().

Excample 3: Java Static Method Taking Arguments And Returning Values

JNI passes and returns object of a basic Coffee type such as int, long, double by value. This is very easy to process. Y'all just have to use the corresponding JNI native blazon jint, jlong, jdouble so on.

And so let's wait at example 3. The Java class is enriched with the following method:

Course MyTest {     ...          public          static          int          mymain2(int          northward) {             for          (int          i=0; i<north; i++)  {                 System.out.print (i);             Arrangement.out.println("          Howdy, World !");         }          return          n*two;                         } }

Calling this function from C++ is very similar to the previous example:

jmethodID mid2 = env->GetStaticMethodID(cls2,          "          mymain2",          "          (I)I");          if(mid2 ==          nullptr) {     cerr          <          <          "          Mistake: method it main2(int) not found !"          <          <          endl; }          else          {     env->CallStaticVoidMethod(cls2, mid2, (jint)5);     cout          <          <          endl; }        

The signature passed to GetStaticMethodID() is now "(I)", saying that it's a part with ane integer statement, followed past "I", i.east., returning an integer. If y'all want to experiment with other types, have a look at the total signature reference documented in Oracle's JNI specifications.

Instance 4: Java Arrays and Objects

As soon as you lot work with a office taking or returning objects that are not of a fundamental blazon, the object is passed by reference. So let'southward take the example of a call the Java main() function, which looks like:

          class          MyTest {     ...          public          static          void          primary (String[] args) {                    } }

Calling this part from C++ is a picayune fleck more than complex. Get-go, the signature of the method: arrays are noted with "[" in the JNI signature parameter. Not built in types are indicated with an "L" followed by the full grade name, followed by a semicolumn. As the function returns void, the signature is hence: "([Ljava/lang/String;)V". Yep ! Now, we tin can remember the method:

            jmethodID mid3 = env->GetStaticMethodID(cls2,          "          main",          "          ([Ljava/lang/String;)5");          if(mid3 ==          nullptr) {      cerr          <          <          "          Fault: method non establish !"          <          <          endl; }        

To call the method, we beginning need to build a Java array, too as for the cordsouth to populate information technology. We do this in the following fashion:

          else          {     jobjectArray arr = env->NewObjectArray(five,                                  env->FindClass("          java/lang/String"),                                env->NewStringUTF("          str"));       env->SetObjectArrayElement( arr,          1, env->NewStringUTF("          MYOWNSTRING"));      env->CallStaticVoidMethod(cls2, mid3, arr);       env->DeleteLocalRef(arr);     }        

The important signal to understand here is that the Java objects are created by the JVM. So the JVM is responsible to costless the retentivity when it is no longer used. Every bit soon every bit you no longer need an object, you should hence call DeleteLocalRef() to tell the JVM that you don't need it anymore. If you lot don't, memory will leak (see the explanations in this StackOverflow question).

Example v: Objects and Methods

Until now, we've kept things simple: nosotros've called but static Java methods. These are independent of the object. Merely this is not the most natural way to get in object oriented programming. So in that location are big chances that some twenty-four hours you will have to create an object and phone call the methods for the object.

Allow's enrich our Coffee grade with a constructor and a uncomplicated method, in example 5:

Class MyTest {     ...          private          int          uid;                 public          MyTest() {               uid = magic_counter++ *          2;     }          public          void          showId() {           System.out.println(uid);     } }

From C++, you lot tin can then create a MyTest object, by finding and invoking a constructor:

jmethodID ctor = env->GetMethodID(cls2,          "          <init>",          "          ()Five");          if(ctor ==          nullptr) {     cerr          <          <          "          ERROR: constructor not found !"          <          <          endl; }          else          {     cout          <          <          "          Object succesfully constructed !"          <          <endl;     jobject myo = env->NewObject(cls2, ctor);        

If the object is successfully synthetic, nosotros can then search for the method we want to phone call, and invoke it for the object:

          if          (myo) {          jmethodID testify = env->GetMethodID(cls2,          "          showId",          "          ()V");          if(show ==          nullptr)              cerr          <          <          "          No showId method !!"          <          <          endl;          else          env->CallVoidMethod(myo, evidence);     } }        

So, now yous know how to launch the JVM, run static methods, create objects and invoke their methods. You are in full command of any Java component that you would similar to integrate with our C++ lawmaking.

In that location'south however a final thing that we need for having a full motion picture...

Example 6: Callbacks and Case Variables

In your Coffee code, y'all could perhaps need to recall C++ functions. This is done with Java native methods. Here a last enhancement of our Java example:

MyTest {      ...          public          native          void          doTest();            public          void          showId() {            System.out.println(uid);          doTest();               } }

The native part is declared in Java, just has to be defined and registered in C++ earlier the Java object is created.

Hither's how such a callback function would be declared in C++:

          void          doTestCPP(JNIEnv*e, jobject o) {          std::cout          <          <          "          C++callback activated"          <          <          std::endl;     jfieldID f_uid = e->GetFieldID(e->GetObjectClass(o),          "          uid",          "          I");          if          (f_uid)          std::cout          <          <          "          UID data member: "          <          <          e->GetIntField(o, f_uid)          <          <          std::endl;          else          std::cout          <          <          "          UID not found"          <          <          std::endl; }

Past the manner, equally you see, nosotros can easily admission object variables using GetFieldId().

To annals the native function mapping, we use the following code snippet:

JNINativeMethod methods[] { {          "          doTest",          "          ()Five", (void          *)&doTestCPP } };            if(env->RegisterNatives(cls2, methods,          1)          <          0) {                                  if(env->ExceptionOccurred())                                               cerr          <          <          "                      OOOOOPS: exception when registreing naives"          <          <          endl;          else          cerr          <          <          "                      ERROR: problem when registreing naives"          <          <          endl; }        

Now, you can call again the method showId(), as in the previous example. But the new version will call doTest() which volition call from Coffee our new C++ callback.

Points of Interest

We tin now organize a bidirectional integration C++ to Java and back. You accept learnt the essentual JNI surviving techniques. Upward to y'all to play with this new cognition. Here, you lot have the full reference of the JNI functions.

While you could at present imagine whatever kind of integration, you should exist enlightened of some performance constraints. JNI means some minimal overhead.

I've written a small benchmark calling the same very small Coffee role either from Java or from C++. It is provided in example 7.

On my core i7, the results are the post-obit:

Java called from Java:          14          nanoseconds/iteration Coffee called from C++:          23          nanoseconds/iteration C++ 100%          native:          ii          nanoseconds/iteration

Each C++ to Java telephone call through JNI has an overhead of 9 to 10 ns. For small functions such as in this benchmark, this overhead is an overkill. And so this kind of integration should non be considered for high frequency, low latency role calls. Just many JNI applications are about integrating high level Coffee components or interfaces. In this case, the JNI overhead is negligible compared to the tremendous benefit of the piece of cake integration.

A concluding point of involvement to keep in heed is the difficulty of memory management with Java objects. Memory could leak if Java objects are created in C++ and the C++ variable referring to it goes out of scope. This is manageable for small demos similar here. Merely for the sake of reliability in more complex software, a C++ wrapper implementing RAII should actually be considered for Coffee objects.

History

  • 19/v/2015 Initial version

This commodity, along with any associated source code and files, is licensed under The zlib/libpng License

masonmotosed.blogspot.com

Source: https://www.codeproject.com/Articles/993067/Calling-Java-from-Cplusplus-with-JNI

0 Response to "Once Java Function Is Called"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel