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…