I. Overview:
This article will briefly introduce how to compile some simple JNI methods. We all know that the JNI method can help us call functions written in C/C ++. If a job has been implemented in C/C ++, we don't need to make great effort to implement this work again in Java, as long as the corresponding JNI function is compiled, java can easily call C/C ++ functions, thus greatly reducing the workload of program developers.
In this project, we have compiled many small instances. By reading and running these small instances, you can easily learn how to compile the JNI method. This document helps you better understand and implement these instances.
Now let's enter the topic. First, let's take a look at the architecture of this project. This project is divided into two parts: the C language and the C ++ language. Each part contains the Java and SRC Source file directories and a MAKEFILE file. The Java directory is a Java source program that needs to call the JNI function and contains the suffix. java. The src directory contains the implementation code of the JNI function, including the. C or. cpp file and. h file. Makefile is a file that compiles and organizes files in the Java and SRC directories to generate executable files. After the MAKEFILE file is executed, the following subdirectories are generated: Lib, class, and bin. The lib directory contains the static library file libjniexamples. So generated by the project. The JNI method called by the Java program is called through this library. The class directory contains the. Class file generated by the. Java file under the Java directory. The bin directory contains an executable shell script file. When the script is executed, the running results of all program instances of the project are displayed on the screen.
The specific steps are as follows:
Make
CD Bin
./Run. Sh
The following describes the instances implemented in this project:
1. How to call functions in Standard C/C ++, for example, printf (...)
2. How to call custom functions in C/C ++
3. How to access the object instance domain in the Java class in the JNI Function
4. How to access static instance domains in Java classes in JNI Functions
5. How to call Java object methods in the JNI Function
6. How to call static methods of Java classes in JNI Functions
7. How to pass basic data type parameters in the JNI Function
8. How to pass object type parameters in the JNI Function
9. How to process strings in the JNI Function
10. How to process arrays in the JNI Function
11. Handling the return values in the JNI Function
12. Create Java class objects in JNI
2. Basic steps:
Before introducing these examples, let's take a look at the basic steps required to compile the JNI method. These instances are explained using C as an example, as for the c ++ instance and the C instance, you only need to make a slight modification. At the end of this document, we will introduce the following content:
1. To define the JNI method, you must first declare this method in Java (naturally, this declaration process must be carried out in the class)
The Declaration format is as follows:
Publicnativevoid print (); system. loadlibrary ("jniexamples ");}
The JNI function is declared using the native method keyword.
2. Compile the source files of this class and use the javac command to generate the corresponding. Class file.
3. Use javah-JNI as the function to generate a conversion stub between Java call and the actual C function. This stub retrieves parameter information from the VM stack, and pass it to the compiled C function for conversion.
4. Create a special shared library and use the system. loadlibrary to load the libjniexamples shared library.
3. Configure the runtime environment:
Before writing a simple JNI function, we must configure the corresponding runtime environment. JDK configuration is not described here. Here we mainly talk about the library path. When system. loadlibrary (...) is called, the compiler searches for this library in the library path set by our system. The path modification method is basically the same as the method for modifying any environment variable. You only need to add a line LD_LIBRARY_PATH =.:./lib: $ (LD_LIBRARY_PATH) in the/etc/bash. bashrc directory. You can also use the command line export LD_LIBRARY_PATH =.:./lib: $ (LD_LIBRARY_PATH)
4. Run instance analysis:
1. Example 1: Call the built-in function printf () in Standard C in JNI ():
The following takes instance 1 as an example to describe the detailed process of compiling the JNI method.
(1) define the print class containing the JNI function. java: {/************************************** * ********************************* the print () function will call the printf () funcion which is a ansi c funciton ********************************* **************************************** */publicnativevoid print ();
System. loadlibrary ("jniexamples ");}}
In the preceding example, a jni method of the print class is defined using the public native void print (); statement. Use the sysgem. loadlibrary ("jniexamples") statement to load the libjniexamples. So library. Note: The loaded statement must be declared in the static block with the static keyword to ensure that the Library is always loaded when the class is referenced.
(2) Compile the class: javac print. java. Generate the print. Class class and use javah to generate a print. h header file: javah print. The print. h file format of Changsheng is as follows:
/* Do not edit this file-it is machine generated * // * Header for class print */jniexport void jnicall java_print_print (jnienv *, jobject );}
The bold font indicates the life part of the JNI function to be implemented.
(3) Compile the implementation Part Of The JNI function print. c jniexport void jnicall java_print_print (jnienv * ENV, jobject OBJ) {printf ("example1: In this example a printf () function in ansi c is called \ n "); printf ("hello, the output is generated by printf () function in ansi c \ n ");}
A simple JNI method is implemented in this file. This method calls the printf () function in ansi c and outputs two sentences.
(4) Compile the local function into the library libjniexamples. So:
Usage statement: gcc-FPIC-I/usr/jdk1.5/include-I/usr/jdk1.5/include/Linux-shared-O libjniexamples. So print. C.
(5) Now all JNI functions have been implemented. You can call and pull them in Java code.
Here we use a simple class to test the implemented JNI method. The following is printtest. java source code: publicstaticvoid main (string [] ARGs) {print P = new print (); p. print ();}}
(6) Compile and execute printtest. Java to obtain the following results:
Example1: In this example a printf () function in ansi c is called
Hello, the output is generated by printf () function in ansi c.
The implementation steps of each instance described below are also performed according to the above steps. So we will only introduce the key part of the implementation.
2. Example 2: Call a function defined by the C language (source code: Java/cfunction. Java/c_functiontest.java src/cfunction. c src/cfunction. h)
To call a function implemented by C in a Java program, you need to define a JNI method in the class that needs to call the C function, calling the C function in the JNI method is equivalent to encapsulating the C function in Java for the Java program to call.
In instance 2, we have simply defined a printhello () function, which outputs only one sentence. To call this function in a Java program, you only need to call it in the JNI function, it is no different from calling the built-in prinf () function in ansi c.
3. Example 3: Access the object instance domain in the Java class in the JNI function (source program: Java/commonfield. java/commonfieldtest. java src/commonfield. c src/commonfield. h)
The implementation of the JNI function is implemented in the C language. If it wants to access the instance domain of the Class Object defined in Java, three steps are required,
(1) Call the getobjectclass () function to obtain the class of the object. The function returns a value of the jclass type.
(2) Call the getfieldid () function to obtain the ID of the Instance domain to be accessed in this class.
(3) Call getxxxfield () to obtain the value of the Instance domain to be accessed. Xxx corresponds to the instance domain to be accessed.
In JNI, the correspondence between the Java programming language and the C language data type is that 'J' is added before the original Java data type to indicate the corresponding C language data type, for example, Boolean is jboolean, Int Is jint, double is jdouble. The corresponding type of object type is jobject.
In this example, you can see in Java/commonfield. java defines the commonfield class, which contains the int A and int B instance domains. We need to access and modify these two domains in the JNI function getcommonfield. You can find the implementation part of the function in src/commonfield. C.
The following statement is used to access the domain (the following code is taken from src/commonfield. C ):
Jclass class_field = (* env)-> getobjectclass (ENV, OBJ); jfieldid FDA = (* env)-> getfieldid (ENV, class_field, "A", "I "); jfieldid FDB = (* env)-> getfieldid (ENV, class_field, "B", "I"); jint valuea = (* env)-> getintfield (ENV, OBJ, FDA); jint valueb = (* env)-> getintfield (ENV, OBJ, FDB );
The env pointer is used to call all JNI functions in JNI. This pointer is also the first parameter of each local method. It is the pointer of the function pointer table. Therefore, you must add (* env)-> getobjectclass (ENV, OBJ) before each JNI call to return the object type, the OBJ parameter indicates the class object you want to obtain.
Jfieldid getfieldid (jnienv * ENV, jclass Cl, const char name [], const char sig []) This function returns a domain identifier name that represents the domain name, and SIG that represents the encoded domain signature. The so-called encoding signature is the encoding type signature. In the above example, the instance domain is int type and is represented by "I". Similarly, "B" indicates byte and "C" indicates char, "D" indicates double, "F" indicates float, "J" indicates long, "S" indicates short, and "V" indicates void, and "Z" indicates Boolean.
Getintfield (ENV, OBJ, FDA) is used to access the FDA domain of the OBJ object. If the domain to be accessed is of the double type, getdoublefield (ENV, OBJ, FDA) is used for access, that is, the type corresponds to XXX in getxxxfield.
The following functions are used to modify the value of a domain:
(* Env)-> setintfield (ENV, OBJ, FDA, 109); (* env)-> setintfield (ENV, OBJ, FDB, 145 );
This is similar to the value of the obtained field, except that the function has a value parameter to be set for this field.
The functions related to accessing the object instance domain are as follows:
Jfieldid getfieldid (jnienv * ENV, jclass Cl, const char name [], const char sig [])
This function returns the identifier of a domain. The parameter meanings are as follows:
Env JNI interface pointer; CL class object; Name Domain Name; sig encoded domain signature
Xxx getxxxfield (jnienv * ENV, jobject OBJ, jfieldid ID)
This function returns the value of the field. XXX is one of the types in object, Boolean, byte, Char, short, Int, long, float, and double.
The env JNI parameter is an excuse pointer; obj is the object where the domain is located; ID is the identifier of the domain.
Void setxxxfield (jnienv * ENV, jobject OBJ, jfieldid ID, xxx value)
This function is used to set the value of a field. The meaning of XXX is the same as above,
The meanings of env, OBJ, and Id are the same as those in the parameter. The value is the value to be set.
4. Example 4: static instance domain of the handler class in the JNI function (Java/field. Java/fieldtest. Java src/field. c src/field. h)
Because the static instance domain does not belong to a certain object but belongs to a class, it is different from the instance domain of the object to access the static instance domain, the function it calls is (in example 4, the code is taken from src/field. c ):
Jclass class_field = (* env)-> findclass (ENV, "field"); jfieldid FDA = (* env)-> getstaticfieldid (ENV, class_field, "", "I"); jint valuea = (* env)-> getstaticintfield (ENV, class_field, FDA); (* env)-> setstaticintfield (ENV, class_field, FDA, 111 );
Because there are no objects, you must use findclass instead of getobjectclass to obtain class references. The second parameter in findclass () is the class encoding signature. The class encoding signature is different from the basic type encoding signature. If the class is in the current package, it is the name of the class. If the class is not in the current package, the detailed path of the class should be added: for example, the string class is in Java. in the lang Package, the string signature must be written as (ljava/lang/string;), where (L and;) is indispensable, where (;) is the terminator of expression. The other three functions are basically the same as accessing the object data domain.
5. Example 5: Call the Java object method in the JNI function (Java/commonmethod. Java/commonmethodtest. Java src/commonmehod. c src/commonmethod. h)
In JNI functions, we not only need to access the data domains of Java objects, but also sometimes need to call methods implemented by class objects in Java. Example 5 is about implementation in this regard. In src/commonmethod. C, we can find the following code:
Jniexport void jnicall java_commonmethod_callmethod (jnienv * ENV, jobject OBJ, jint A, jstring s) {printf ("Example 5: In this example, A object's method will be called \ n "); jclass class_commonmethod = (* env)-> getobjectclass (ENV, OBJ); jmethodid MD = (* env) -> getmethodid (ENV, class_commonmethod, "print", "(iljava/lang/string;) V ");
(* Env)-> callvoidmethod (ENV, OBJ, MD, A, S );}
The Code section shows how to call Java class object functions. From the code above, we can see that there are three steps to implement this call: Call the three functions jclass class_commonmethod = (* env)-> getobjectclass (ENV, OBJ ); jmethodid MD = (* env)-> getmethodid (ENV, class_commonmethod, "print", "(iljava/lang/string;) V"); (* env) -> callvoidmethod (ENV, OBJ, MD, A, S );
The getobjectclass (...) function obtains the class of the object to be called. getmethodid (...) obtains the ID of the method to be called relative to the class. callxxxmethod (...) calls this method.
When writing this call process, you must note that it is still getmethodid (...) in this example, we need to find the print (int A, string s) method of the commonmethod class. This method prints the integer, and string S. Getmethodid (ENV, class_commonmethod, "print", "(iljava/lang/string;) V "); from left to right, you can see that the content in the brackets is the parameter content of the method to be called. I indicates that the first parameter is of the int type, "ljava/lang/string; "indicates that the second parameter is of the string type, and" V "indicates that the return value type is null void. If the return value type is not empty, the corresponding type signature is used. The return value type is the same as the callxxxmethod (...) function that will be used to call this method (...) associated, XXX of this function should be replaced with the corresponding type, in this instance void, if the return value type is int, the function that calls this method is callintmethod (...).
6. Example 6: Call the static method of Java class in the JNI function (Java/method. Java/methodtest. Java src/method. h src/method. c)
Example 5 describes how to call a method of a class object. In this example, we will introduce how to call a static method of a Java class. In this example, we use/Java/method. java defines static methods:
Public static void print (){
System. Out. println ("this is a static method of class method ");
}
The function is to print the string "this is a static method of class method ";
In src/method. c implements the JNI function called for this method: jniexport void jnicall java_method_callmethod (jnienv * ENV, jobject OBJ) {printf ("Example 6: In this example, the class's static method will be called \ n "); jclass class_method = (* env)-> findclass (ENV," method "); jmethodid MD = (* env) -> getstaticmethodid (ENV, class_method, "print", "() V ");
(* Env)-> callstaticvoidmethod (ENV, class_method, MD );}
Different from instance 5, the three functions to be called become:
Findclass (...), getstaticmethodid (...), callstaticvoidmethod (...).
The mechanism is the same as instance 5. I will not introduce it too much again.
7. Example 7: passing basic data type parameters in the JNI function (Java/basic. java/basictest. java src/basic. c src/basic. h) in Java/basic. in Java, we define a public native void raisevalue (int A) function, which will print to increase the value of A and print the original value and the new value.
The implementation of the JNI function is provided in src/basic. C. Jniexport void jnicall java_basic_raisevalue (jnienv * ENV, jobject OBJ, jint A) {printf ("Example 7: In this example, A integer type parament will be passed to the JNI Method \ n "); jclass class_basic = (* env)-> getobjectclass (ENV, OBJ); jfieldid FD = (* env) -> getfieldid (ENV, class_basic, "value", "I ");
Jint v = (* env)-> getintfield (ENV, OBJ, FD); V = V + A; (* env)-> setintfield (ENV, OBJ, FD, V );}
In this function implementation, the getobjectclass (...), getfieldid (...), getintfield (...) to obtain the value of a function, the following "= V + A;" indicates that the processing method for passing basic type parameters is the same as that for processing basic data types in C language.
8. Example 8: passing object type parameters in the JNI function (Java/book. Java/booktest. Java src/booktest. c src/booktest. h)
This example demonstrates the process of passing object functions in the JNI function.
We have defined a class Book total_page = T;} publicint gettotalpage () {} publicint getcurrentpage () {} current_page ++;} in this instance ;}}
Then we define the JNI function in Java/booktest. java.
Public native void bookcurrentstatus (book B );
This function requires a book-type parameter and returns the current status of the parameter, including the total number of pages of the book in total_page and the current page current_page. The implementation part of the function is (src/booktest. c)
Jniexport void jnicall java_booktest_bookcurrentstatus (jnienv * ENV, jobject this_obj, jobject OBJ) {printf ("Example 8: In this example, a object parament will be passed to the JNI method. \ N "); jclass class_book = (* env)-> getobjectclass (ENV, OBJ );
Jmethodid id_gettotal = (* env)-> getmethodid (ENV, class_book, "gettotalpage", "() I"); jmethodid id_getcurrent = (* env)-> getmethodid (ENV, class_book, "getcurrentpage", "() I"); jint total_page = (* env)-> callintmethod (ENV, OBJ, id_gettotal); jint current_page = (* env) -> callintmethod (ENV, OBJ, id_getcurrent );
Printf ("the total page is: % d and the current page is: % d \ n", total_page, current_page );}
This function contains three parameters (jnienv * ENV, jobject this_obj, jobject OBJ), and the second jobject this_obj parameter indicates the Class Object of the current JNI function, the third jobject OBJ parameter indicates the Class Object of the book type passed.
For the implementation part, it is basically the same as the operation in the method of instance 5-calling Java class object, so I will not explain it in detail.
9. Example 9: Process strings in the JNI function (Java/Str. Java/strtest. Java src/Str. c src/Str. h)
In this example, we will explain how to pass and process string parameters.
In Java/Str. Java, we define a printstring (string s) method to process string parameters.
In src/Str. in C, we can see the implementation part of this function: jniexport void jnicall java_str_printstring (jnienv * ENV, jobject OBJ, jstring s) {printf ("Example 9: In this example, A String object parament will be passed to the JNI method. \ n "); string = (char *) (* env)-> getstringutfchars (ENV, S, null ); printf ("% s is put out in native method \ n", string );
(* Env)-> releasestringutfchars (ENV, S, (jbyte *) string );}
Two functions are called during implementation: getstringutfchars (...) and releasestringutfchars (...).
Getstringutfchars (...) is used to obtain the string of the string object and capture it as a char * type. The string can be processed and pulled in C. Releasestringutfchars (...) is used to recycle the string after it is used. Remember, do not forget to call this function when using a string.
10. Example 10: Process arrays in the JNI function (Java/ARR. Java/arrtest. Java src/ARR. c src/ARR. h)
All array types in Java have corresponding C language types, where jarray represents a Generic Array
Boolean [] -- jbooleanarray byte [] -- jbytearray char [] -- jchararary
Int [] --- jchararray short [] --- jshortarray long [] --- jlongarray float [] -- jfloatarray
Double [] -- jdoublearray object [] --- jobjectarray. When accessing an array, you can use the getobjectararyelement and setobjectarrayelement methods to access the elements of the object array.
For general array types, you can call getxxxaryelements to obtain a pointer that only wants the starting element of the array. When you are not using this array, remember to call releasexxxarrayelements, in this way, your changes can be reflected in the original array. If you need to get the length of the array, you can call the getarraylength function.
In this instance. define a local method in Java: Print (INT intarry []). This function outputs the array, in src/ARR. in C, we can see that the implementation process of this method is as follows:
Jniexport void jnicall java_arr_print (jnienv * ENV, jobject OBJ, jintarray intarray) {printf ("Example 10: In this example, a array parament will be Pasto sed the JNI method. \ n "); arr = (* env)-> getintarrayelements (ENV, intarray, null); // n = (* env)-> getarraylength (ENV, intarray );
Printf ("the native method output the int array \ n"); for (I = 0; I <(* env)-> getarraylength (ENV, intarray); I ++) {printf ("% d", arr [I]);} (* env)-> releaseintarrayelements (ENV, intarray, arr, 0 );}
Here we call getintarrayelements (...) to get a pointer to the first element of the intarray [] array.
Use the getarraylength (...) function to obtain the length of the array, so as to facilitate the array time. Finally, use the releasearrayelements (...) function to release the array pointer.
11. Example 11: Return Value in JNI (Java/returnvalue. Java/returnvaluetest. Java/bookclass. Java src/returnvalue. c src/returnvalue. h)
Three JNI methods are defined in the Java/returnvalue class: returnint (), returnstring (), returnobject ()
Return the int, string, and object values respectively.
The implementation in src/returnvalue. C is as follows:
Jniexport jint jnicall evaluate (jnienv * ENV, jobject OBJ) {jclass class_returnvalue = (* env)-> getobjectclass (ENV, OBJ); jfieldid FD = (* env) -> getfieldid (ENV, class_returnvalue, "value", "I"); jint v = (* env)-> getintfield (ENV, OBJ, FD );
} * Signature: () ljava/lang/string; jniexport jstring jnicall java_returnvalue_returnstring (jnienv * ENV, jobject OBJ) {printf ("example 11: In this example, the int and object of return value will be proceeding \ n "); jclass class_returnvalue = (* env)-> getobjectclass (ENV, OBJ );
Jfieldid FD = (* env)-> getfieldid (ENV, class_returnvalue, "name", "ljava/lang/string;"); jstring jstr = (jstring) (* env) -> getobjectfield (ENV, OBJ, FD);} ** method: returnobject jniexport jobject jnicall java_returnvalue_returnobject (jnienv * ENV, jobject OBJ)
{Jclass class_returnvalue = (* env)-> getobjectclass (ENV, OBJ); jfieldid FD = (* env)-> getfieldid (ENV, class_returnvalue, "mybook", "lbookclass; "); jobject jbook = (jstring) (* env)-> getobjectfield (ENV, OBJ, FD );}
The access to common parameters, string parameters, and object parameters of Java objects is involved here.
12. Example 12: Create a Java Class Object in JNI: (Java/test. Java src/createobj. c src/createobj. h)
If you want to create a Java class object in the JNI function, you must reference the Java class constructor method and call the newobject function.
The newobject function is called as follows:
Jobject obj_new = (* env)-> newobject (ENV, class, methodid, paraments );
In this instance, we define the book1 class in Java/test. Java and create this class object in the modifyproperty () jni method of the createobj class. We can see in src/createobj. C that the JNI method creates an object process:
Jobject book; jclass class_book; jmethodid md_book; class_book = (* env)-> findclass (ENV, "lbook1;"); md_book = (* env)-> getmethodid (ENV, class_book, "<init>", "(iiljava/lang/string;) V"); book = (* env)-> newobject (ENV, class_book, md_book, 100,1, "Huanghe ");
During object creation, you can see that to create a Java class object, you must first obtain the class using the findclass function, and then use the getmethodid method to obtain the constructor method ID of the class, at this time, the constructor's function name is always: "", and the signature of the function must comply with the function signature rules. Here, our constructor has three parameters: int, Int, String.
And its return value type must be permanently null, so the function signature is: "(iiljava/lang/string;) V"
Then we call the newobject () function to create an object of this class. After that, we can use this object to pull.
The above describes the implementation example of the JNI Function C language. To use a C ++ instance, we only need to slightly modify each function call process:
Example: (* env)-> newobject (ENV, class_book, md_book, "Huanghe ");
Change to: (ENV)-> newobject (class_book, md_book, "Huanghe ");
That is, change (* env) to (ENV) and then remove the Env from the parameter. Then, change all C functions to C ++ functions and then pull them.
For more information, see the C ++ instance code.
From: http://blog.chinaunix.net/u1/38994/showart_1099528.html