📜 ⬆️ ⬇️

JNI: Making friends with Java and C ++

Introduction


There are times when in Java some actions are performed outside of the usual Java classes. For example, you need to execute code written in C / C ++ or any other language.

In this article we will consider this question from a practical point of view, namely, we will write a simple example of the interaction of Java code with C ++ code using JNI . The article does not contain something supernatural, it is rather a reminder for those who have not worked with it.

For our purposes, there is the possibility of dynamic loading of native libraries, called by the System.load() method, which can be found in more detail here .

Formulation of the problem


Suppose we need to implement a class that contains a native method that displays “Hello world”.
')
JNIHelloWorld.java

 package ru.forwolk.test; public class JNIHelloWorld { native void printHelloWorld(); } 

Header generation


Generate the headers of this class for C ++.

First, create a folder in the root of the project, where we will collect the binaries:

 mkdir bin 

Then, compile our class in this directory.

 javac -d bin/ src/ru/forwolk/test/JNIHelloWorld.java 

In the bin folder we have a class file. Rather, in bin / ru / forwolk / test /. Go to the bin folder and generate the headers.

 cd bin/ javah ru.forwolk.test.JNIHelloWorld 

As you can see, the ru_forwolk_test_JNIHelloWorld.h file appeared in our bin folder. For simplicity, rename it to JNIHelloWorld.h

 mv ru_forwolk_test_JNIHelloWorld.h JNIHelloWorld.h 

Opening it, we see the following picture:

 JNIHelloWorld.h /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class ru_forwolk_test_JNIHelloWorld */ #ifndef _Included_ru_forwolk_test_JNIHelloWorld #define _Included_ru_forwolk_test_JNIHelloWorld #ifdef __cplusplus extern "C" { #endif /* * Class: ru_forwolk_test_JNIHelloWorld * Method: printHelloWorld * Signature: ()V */ JNIEXPORT void JNICALL Java_ru_forwolk_test_JNIHelloWorld_printHelloWorld (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif 

C ++ implementation


Create a source file JNIHelloWorld.cpp. I created a project for this purpose in Clion, into which I inserted the necessary file. We implement our method.

 JNIHelloWorld.cpp #include <iostream> #include "JNIHelloWorld.h" JNIEXPORT void JNICALL Java_ru_forwolk_test_JNIHelloWorld_printHelloWorld (JNIEnv *, jobject) { std::cout << "Hello world!"; } 

In order for Clion to work correctly, you need to add Java libraries to the CMakeLists.txt file:

 //  $JAVA_HOME --   Java include_directories($JAVA_HOME/include) include_directories($JAVA_HOME/include/linux) link_directories($JAVA_HOME/include) link_directories($JAVA_HOME/include/linux) 

Next, we compile

 g++ -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -fPIC JNIHelloWorld.cpp -shared -o helloworld.so -Wl,-soname -Wl,--no-whole-archive 

Java download


In the root folder of the project appeared file helloworld.so. Move it to the bin / java project folder.

Now you need to download our library. A good practice would be a static library load right in the classroom. Add the download directly to the JNIHelloWorld class

 static { // $PROJECT_ROOT --     System.load("$PROJECT_ROOT/bin/helloworld.so"); } 

Now we can fully use this class. Let's check.

 public static void main(String[] args) { JNIHelloWorld p = new JNIHelloWorld(); p.printHelloWorld(); } 

At the output we get

 Hello world! Process finished with exit source 0 

Parameter passing


And what to do if we need not only to execute some code, but also to pass parameters and get an answer? Consider another method that multiplies two numbers. Add a JNIHelloWorld method to the class.

 native int multiply(int a, int b); 

Perform the same steps described above for generating headers. As you can see, the following was generated

 /* * Class: ru_forwolk_test_JNIHelloWorld * Method: multiply * Signature: (II)I */ JNIEXPORT jint JNICALL Java_ru_forwolk_test_JNIHelloWorld_multiply (JNIEnv *, jobject, jint, jint); 

Implement the method in JNIHelloWorld.cpp

 JNIEXPORT jint JNICALL Java_ru_forwolk_test_JNIHelloWorld_multiply (JNIEnv *, jobject, jint a, jint b) { return a * b; } 

Again, we will perform the actions described above to pull up the library, add a line to main to output the result of the product of two numbers, and run

 public static void main(String[] args) { JNIHelloWorld p = new JNIHelloWorld(); System.out.println(p.multiply(2, 2)); p.printHelloWorld(); } 

What we get in the console

 4 Hello world! Process finished with exit source 0 

Conclusion


We have examined the possibility of Java using code written in C / C ++. This can be used for various purposes, for example, to increase the speed of code execution, to protect the code from direct interference, and for other purposes. I really hope that this article will help you understand the basics of JNI.

I laid out all the code in open access . In the cpp directory I placed the C ++ class without the extra files of the Clion project.

additional literature


Also for more outlook on this topic I recommend to pay attention to the following articles:
JNI, loading native libraries. Change java.library.path on the fly
Is the native method expensive? JNI "Secret" extension

Source: https://habr.com/ru/post/353294/


All Articles