JNI introduction series I, the first JNI program

Posted by GreenMarch on Sun, 19 Jan 2020 11:52:11 +0100

First, write a simple class, which contains a native method

public class JNI{
	static{
		// Load the dynamic link library, which we will create below
		System.loadLibrary("hello_jni");
	}	
	public native void hello();
	
	public static void main(String[] args){
		JNI jni = new JNI();
		jni.hello();
	}
}

Now let's generate the corresponding dynamic link library. Note that we are using Linux. The dynamic library under Linux ends with. so or. so.y, where y represents the version number and. dll under Windows.

Compile java files and generate Java header files

# Compile class file
javac JNI.java 
# Generate the header file corresponding to the native method. The header file name starts with the class name by default. For example, the generated header file name here is JNI.h
javah JNI -o 

Generated header file

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNI */

#ifndef _Included_JNI
#define _Included_JNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JNI
 * Method:    hello
 * Signature: ()V
 */
 /** 
  * This function declaration corresponds to the native method we wrote in the java file
  * We need to implement the function declaration. If there are multiple native methods in the java file above, multiple function declarations will appear here
  * The function name is a fixed format: Java class fully qualified name method name
  * 
  */
JNIEXPORT void JNICALL Java_JNI_hello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

JNIEnv pointer
JNIEnv refers to the Java Native Interface Environment. It is a JNI interface pointer and a function table of the local method. Each member of the function table points to a JNI function. The local method accesses the data structure in the JVM through the JNI function, as shown in the figure below:
The implementation of JNIEnv pointer in < JNI. H > file is a structure containing many JNI functions. The partial summary is as follows:

/*
 * We use inlined functions for C++ so that programmers can write:
 *
 *    env->FindClass("java/lang/String")
 *
 * in C++ rather than:
 *
 *    (*env)->FindClass(env, "java/lang/String")
 *
 * in C.
 */

struct JNIEnv_ {
    const struct JNINativeInterface_ *functions;
    jint GetVersion() {
        return functions->GetVersion(this);
    }
    jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
                       jsize len) {
        return functions->DefineClass(this, name, loader, buf, len);
    }
    ...
     jclass GetObjectClass(jobject obj) {
        return functions->GetObjectClass(this,obj);
    }
    jboolean IsInstanceOf(jobject obj, jclass clazz) {
        return functions->IsInstanceOf(this,obj,clazz);
    }

    jmethodID GetMethodID(jclass clazz, const char *name,
                          const char *sig) {
        return functions->GetMethodID(this,clazz,name,sig);
    }
    ...
}

Job object and jclass type
Jobject and jclass are usually the second parameters of JNI functions. When the declared Native method is a static method, the corresponding parameter jclass. Because the static method does not depend on the object instance, but on the class, the parameter passes a jclass type. On the contrary, if the declared Native method is a non static method, the corresponding parameter is jobject.

Create a new jni.c file to implement the header file above

#include<stdio.h>
// Import the generated header file above
#include<JNI.h>
/**
 * Functions to implement header file declaration
 */
JNIEXPORT void JNICALL Java_JNI_hello
  (JNIEnv * env, jobject obj){
	printf("hello jni\n");
}

Generate the dynamic link library file libhello_jni.so. The name here should correspond to the name of the dynamic link library we loaded above, that is, the underlined part of lib_.so. This is the naming form of the dynamic link library loaded by c language, which has nothing to do with java.

These command parameters are GCC parameters. You need to know more about them

# -I is the path to the specified header file - o is the specified output file name
# -fPIC tells the compiler to generate position independent code
# -shared is to generate dynamic link library
gcc -fPIC -shared jni.c -I $JAVA_HOME/include -I ./ -I $JAVA_HOME/include/linux -o libhello_jni.so

Finally, we get a dynamic link library named libhello? Jni.so

At this time, we can try it out

There's a mistake. Why does it happen? The error prompt is very clear. It's just that the DLL is not found. It's right to think about it. Why can we find it directly by System.loadLibrary("")? Its loading dynamic link library has its own rules. The path we generate dynamic link library above is the current path of JNI.class, that is, the path when we run java JNI command.
Java.library.path appears above. Its loading dynamic link library is to find the corresponding dynamic link library from the path corresponding to the java.library.path attribute. How to set the path?

1. Start JVM to set parameters

java -Djava.library.path=Route JNI

2. After / etc/profile, add a line export LB? Library? Path = path

In this way, it can be successfully loaded into the dynamic link library

156 original articles published, 44 praised, 8221 visited
Private letter follow

Topics: Java Linux jvm Windows