For the sake of efficiency, in many cases we need to invoke the underlying C or C + + implementations in the upper Java code, and JNI will be able to do its thing. JNI (Java Native Interface) allows Java code to interact with code written in other languages, using Java to interact with locally compiled code, often losing platform portability. However, in some cases it is acceptable and even necessary. For example, use some old libraries to interact with the hardware, the operating system, or to improve the performance of the program.
Steps for using JNI programming
- Declaring a local method with the native keyword in Java code
- Run Javah, get the C language header file that contains the method declaration (using the C function name in JNI programming is usually related to the Java method has some naming rules, we will introduce later, we use Javah to help us get the method name)
- Use C or C + + to implement the functionality we need
- To generate a shared library file, a shared library file can be a Windows-style. dll file, or it can be a UNIX-style. so file
- To ensure that the virtual machine loads the library before the class is first used, the static initialization block is used to load
About Javah: Run the javah-h command in the command window we can see all the options for Javah, here's a brief introduction:
- -classpath, the path for loading classes
- -D, Output directory
- -O, output files,-D and-O can only use one of them
- -jni, creating a JNI-styled header file (default)
Writing the first JNI program
See the steps above to do it yourself, using JNI to write a simple Hello World program.
1. First, we write a local method hello in Java code:
package com.example.jnitest;publicclass JniTest { publicstaticnativevoidhello();}
2. Next we use Javah to generate the header file, it should be noted that we should first compile the project to get the. class file. Then we run the command line to the project directory/bin directory, enter the command Javah-classpath. Com.example.jnitest.JniTest get the header file,
The code to get the header file is as follows:
/* do not EDIT this file-it was machine generated */ #include <jni.h> /* Header for Class Com_example_ Jnitest_jnitest */ #ifndef _included_com_example_jnitest_jnitest Span class= "Hljs-preprocessor" > #define _included_com_example_jnitest_jnitest #ifdef __cplusplus extern "C" { #endif /* * class:com_example_jnitest_jnitest * Method:hello * Signature: () V */ jniexport void Jnicall Java_com_example_jnitest_jnitest_hello (jnienv *, jclass); #ifdef __cplusplus } #endif #endif
3. Next we will implement our program in C language, in order to facilitate the creation of shared library files, here I created a console project in VS2012, and select DLLs and empty projects in the application settings.
Then add the header file you just got and create a new CPP file to implement our program.
#include"jni_hello.h"void JNICALL Java_com_example_jnitest_JniTest_hello (JNIEnv *, jclass){ printf("Hello Jni!\n");}
Note that if we add the header file directly may not find the head file jni.h, then we need to configure our project, right-click Project –> Properties –>vc++ directory, select Include directory –> edit, the JNI header file to add the directory, The directories you typically need to add are the Include folder and the Include/win32 folder under the JDK directory.
4. Generate the shared library file, next we just need to run the project to find the. dll file in the Debug folder, it should be noted that vs directly generated a 32-bit DLL file, if your machine is 64 bit will be error can ' t load IA 32-bit. dll on a AMD 64-bit platform, then we'll configure our own VS Engineering: Build –> Configuration Manager's Active solution platform, active solution platform –> new –> Select x64
Next we need to add our shared library:
- First we call SYSTEM.OUT.PRINTLN (System.getproperty ("Java.library.path")); Get the path to the shared library
- Next we copy the DLL file to one of the paths
5. Load the shared library and call the native method:
package com.example.jnitest;publicclass Main { static{ System.loadLibrary("JniApplication");//静态初始化块加载库 } publicstaticvoidmain(String[] args) { JniTest.hello(); }}
Output results in console:
Data type correspondence Relationship in JNI
Knowing the simple usage of JNI, let's take a closer look: first, the correspondence between the Java primitives and the C basic types in JNI
In addition to the basic data types, which are commonly used as string types, string in Java corresponds to jstring in C, we can use the following method to convert the jstring type and char * to each other
char"Hello Jni!"; //创建一个jstring类型字符串 jstring j_string = env->NewStringUTF(ff); //将jstring类型转化为char* constchar0); printf(c_string); //当我们不再使用字符串时,要将其释放 env->ReleaseStringUTFChars(j_string, c_string);
Access to domains in Java
Using the C language to access the fields and methods in Java is to know its ID, which is relatively complex to use, here is a simple example, suppose our class people has an int type of domain age, namely:
package com.example.jnitest;publicclass People { privateint age; publicintgetAge() { return age; } publicvoidsetAge(int age) { this.age = age; }}
Modify the methods in Jnitest, this time by calling the native method to make the age domain twice times the original:
package com.example.jnitest;publicclass JniTest { publicstaticnativechangeAge(People people);}
After rebuilding the header file and then writing our method in the CPP file, each time we access the domain in Java we need to get fieldid, the steps are:
1. Get the class for an implicit parameter
2. Get the domain ID
3. Accessing the value of a domain
#include"Jni_hello.h"Jniexport J Object jnicall java_com_example_jnitest_jnitest_changeage (JNIEnv *env, J class cl, jobject ob) {J class cl_people = env,getobjectclass(ob);Jfieldid C_ageid = Env->getfieldid (Cl_people,"Age","I"); Jint c_age = Env->getintfield (Ob,c_ageid);//Print the modified statementprintf"%d", c_age);//Modify Age domainEnv->setintfield (ob, C_ageid,2*c_age);returnOB;}
The second parameter in the Env->getfieldid method represents the name of the field, and the third parameter indicates that the age field is of type int, which is related to the encoded signature of this JNI, which is mentioned later. This is then called in the Main method:
package com.example.jnitest;publicclass Main { static{ System.loadLibrary("JniApplication"); } publicstaticvoidmain(String[] args) { new People(); people.setAge(5); people = JniTest.changeAge(people); System.out.println("changed::"+people.getAge()); }}
Output Result:
As you can see, the result turns out to be twice times the original. For access to a static domain, you only need to change Env->getintfield to Env->getstaticintfield.
Access to methods in Java
We have just used the method of accessing the domain to modify the value of the domain, below we call the People class in the CPP file Setage method to modify the value of age; Calling Java methods requires the following steps:
- To get the class of an implicit argument
- Get method ID
- Make a call
#include"Jni_hello.h"Jniexport J Object jnicall java_com_example_jnitest_jnitest_changeage (JNIEnv *env, J class cl, jobject ob) {J class cl_people = env,getobjectclass(ob);Jfieldid C_ageid = Env->getfieldid (Cl_people,"Age","I"); Jint c_age = Env->getintfield (Ob,c_ageid);//The third parameter is also related to the encoded signatureJmethodid C_setageid = Env->getmethodid (Cl_people,"Setage","(I) V");The //void represents the return type and, if returned as int, calls the CallintmethodEnv->callvoidmethod (Ob,c_setageid,2*c_age);returnOB;}
Called in the main function, the code is the same as above, the result is as follows:
For a call to the static method, we just need to replace the method at the time of the call, for example, if the Setage method is static, just Env->callvoidmethod (ob,c_setageid,2*c_age); Change to env- >callstaticvoidmethod (Ob,c_setageid,2*c_age);
JNI coded Signature
We mentioned the third parameter in the Env->getfieldid method, which represents the data type of the field with an encoded signature, and now describes the correspondence between data classes in JNI.
- Basic data type:
B--byte, C--char, d--double, F--float,
I ——-int, J--long, S--short, V--–void, Z--boolean
- The array type, at the beginning of the "[" to indicate, such as int[] array is "[i"; a two-dimensional array int[][] is "[[i]
- Class type, with "L + Full package name + class name +;" Represents all of these "." Change to "/", such as Com.example.jnitest.People class represented as lcom/example/jnitest/people;
The encoding signature rule for a method is "(encoded signature of incoming parameter type) + RETURN encoded signature of parameter type", and multiple incoming parameters are directly connected to each other; For example, in our Setage method, the passed-in parameter is int and the return is void, which is represented as "(I) V".
Java programming-Everything you need to know about JNI