We often use ides, such as Eclipse and Android Studio, when developing JNI applications, because tools simplify the development process and increase productivity, but make it harder to see what is really going on. Therefore, this article does not use an IDE for JNI development, so that we can have a more comprehensive understanding of native JNI.
The examples in this article are running under Ubuntu 16.04.
New project
First we create a new project directory called JNIDemo, and then we go to this directory
bxll:~$ mkdir JNIDemo
bxll:~$ cd JNIDemo/
Copy the code
In the JNIDemo directory, create a new file called hello.java with the following code
package com.bxll.jnidemo;
public class Hello {
static {
System.loadLibrary("hello_jni");
}
static native String helloFromJNI(a);
public static void main(String[] args) { System.out.println(helloFromJNI()); }}Copy the code
First, in the static code block, load a library called hello_jni with the system.loadLibrary () method. On Linux, the full name of the library is libhello_jni.so on Windows, This library is called hello_jni.dll. Since I’m using Ubuntu, the name of this library will have to be libhello_jni.so when I compile it later.
We then define a native method, helloFromJNI(), which needs to be implemented in the dynamic library, as we’ll see later.
Finally, in the main() method, the native method is called and the return result of the method is printed.
Compiling Java files
Before compiling the hello. Java file, we need to create a directory to hold the bytecode files, which we’ll call classes for now
bxll:~/JNIDemo$ mkdir classes
Copy the code
We then output the bytecode files generated by the compilation to this directory
bxll:~/JNIDemo$ javac -d classes/ Hello.java
Copy the code
The -d parameter of javac indicates the output directory. For more parameters, see javac.
Generate header file
Before we can generate a header file, we must ask one question: why do we generate a header file? Because there is a one-to-one correspondence between functions declared in header files and native methods declared in Java files, the virtual machine automatically establishes this connection for us. This is also known as static registration.
To generate the header file, we need to create a directory jni to store the header file
bxll:~/JNIDemo$ mkdir jni
Copy the code
Then specify the directory to generate the header file as Jni
bxll:~/JNIDemo$ javah -classpath classes/ -d jni/ com.bxll.jnidemo.Hello
Copy the code
Javah’s -classpath argument specifies the directory where the bytecode is stored, the -d argument specifies the directory where the header file was generated, and finally com.bxll.jnidemo.Hello specifies the full path to the bytecode file. For more javah command parameters, see Javah.
Ok, now that the header file is generated, let’s take a look at its contents. The simplified version looks like this
// com_bxll_jnidemo_Hello.h
/* * Class: com_bxll_jnidemo_Hello * Method: helloFromJNI * Signature: ()Ljava/lang/String; * /
JNIEXPORT jstring JNICALL Java_com_bxll_jnidemo_Hello_helloFromJNI
(JNIEnv *, jclass);
Copy the code
We only need to worry about the function prototype (everything else is C/C++ related), Because Java_com_bxll_jnidemo_Hello_helloFromJNI is the corresponding hello.java native method helloFromJNI().
In addition, we can also see three lines of comment, the first comment Class: Com_bxll_jnidemo_Hello specifies which Java class this function is associated with, the second comment helloFromJNI specifies which native method is implemented, and the third argument ()Ljava/lang/String; Represents the signature of a Java class’s native method in JNI.
Both JNIEXPORT and JNICALL are macros, because different platforms have different specifications for calling methods in dynamic libraries, and these two macros are intended for compatibility processing. On Linux, these two macros are not really useful because both macros are defined as empty.
Implementation header file
Now that you know the function prototype from scratch, you’re ready to implement it
// com_bxll_jnidemo_Hello.cpp
#include "com_bxll_jnidemo_Hello.h"
extern "C" JNIEXPORT jstring JNICALL Java_com_bxll_jnidemo_Hello_helloFromJNI (JNIEnv * env, jclass clazz)
{
const char * str_hello = "Hello from C++";
return env->NewStringUTF(str_hello);
}
Copy the code
This is how JNI generates strings, but I won’t go into detail here.
Compiling dynamic libraries
Now that we have implemented the underlying functions, we need to package these into libraries for easy loading by the Java layer. We chose to package the source file as a dynamic library, but before we can do this, we must ensure that the Java development environment for the operating system is properly deployed, preferably with the JAVA_HOME environment variable set. Let’s look at my JAVA_HOME environment variable first
bxll:~/JNIDemo$ echo $JAVA_HOME
/usr/lib/jvm/java-8-openjdk-amd64/
Copy the code
So, let’s build the dynamic library
bxll:~/JNIDemo$ g++ -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -fPIC -shared jni/com_bxll_jnidemo_Hello.cpp -o jni/libhello_jni.so
Copy the code
G++ -i indicates the location of the JNI header, -fpic indicates the location of the independent code, -shared indicates the location of the dynamic library, and -o indicates the directory and name of the dynamic library. On Linux, the dynamic library name is libxxx. so.
To run the program
Now that the dynamic library is generated, can you run the program? Let’s try it out
bxll:~/JNIDemo$ java -classpath classes/ com.bxll.jnidemo.Hello
Exception in thread "main" java.lang.UnsatisfiedLinkError: no hello_jni in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at com.bxll.jnidemo.Hello.<clinit>(Hello.java:6)
Copy the code
Java. Lang. UnsatisfiedLinkError is to tell you there is no link, of dynamic link library and the reasons why no hello_jni behind in Java library. The path, Java.library. path did not find a dynamic library named hello_jni. On Linux, libhello_jni.so was not found.
Now that we know the reason is that the library was not found in the directory specified by the java.library.path property, I can put the generated library in the specified path and that’s it. Yes, you can, but it’s a bit of a hassle. On Linux, you can add the library’s path to the LD_LIBRARY_PATH environment variable, and programs will search for libraries in that path.
LD_LIBRARY_PATH is null because I haven’t customized my libraries yet, so I’ll set it now
bxll:~/JNIDemo$export LD_LIBRARY_PATH=./jni/
Copy the code
We specify the library search path to the jNI directory in the current directory, because that’s where we just printed the dynamic library.
Now run the Java program again and you should see the desired effect
bxll:~/JNIDemo$ java -classpath classes/ com.bxll.jnidemo.Hello
Hello from C++
Copy the code
The results speak for themselves.
read
Static and dynamic libraries for Linux
Cs-fundamentals.com/c-programmi…