There are many basic methods and processes of how to use JNI on the Internet. If you don't know much about JNI, what JNI does, and how to establish a basic JNI program, you may refer to the following articles:
The simplest example of using VC++6.0 to realize JNI
HelloWorld of JNI introductory tutorial
SUN JNI Tutorial
In the examples of these materials, most of them just input some simple parameters and obtain no parameters. In the actual use process, it is often necessary to process and convert the parameters. Can be recognized by C/C + + programs. For example, we have a structure (structure) DiskInfo in C + +, which needs to pass a parameter similar to DiskInfo *pDiskInfo. How can parameters similar to C + + be passed to Java? Let's discuss the conversion of some common parameters from C + + to Java methods:
Define Native Java classes:
If you are used to using JNI, you won't find it difficult. Since native methods are implemented by other languages, they have no function body in Java. However, all local code must be declared with local keywords and become members of Java classes. Suppose we have such a structure in C + +, which is used to describe the hard disk information:
// Hard disk information struct { char name[ 256 ]; int serial; } DiskInfo;
Then we need to define a class in Java to match it. The declaration can be written as follows:
class DiskInfo { // name public String name; // serial number public int serial; }
In this class, some Native local methods are declared to test the transfer of method parameters. Some functions are defined to transfer structure or structure array. The specific definitions are as follows:
/* ***************** Define local methods******************* */ // Enter common numeric types (Boolean,Byte,Char,Short,Int,Float,Double) public native void displayParms(String showText, int i, boolean bl); // Call a static method public native int add( int a, int b); // Enter an array public native void setArray(boolean[] blList); // Returns an array of strings public native String[] getStringArray(); // Returns a structure public native DiskInfo getStruct(); // Returns an array of structures public native DiskInfo[] getStructArray();
Compile and generate C/C + + header files
After defining the Java class, the next step is to write local code. The local method symbol provides a header file that meets the Convention, using Java tool Javah You can easily create it without having to create it manually. If you use the javah command on the Java class file, a corresponding C/C + + header file will be generated for you.
1. Enter the work path under the console. The path of the project is E:\work\java\workspace\JavaJni.
2. Run the javah command: javah -classpath E:\work\java\workspace\JavaJni com.sundy.jnidemo ChangeMethodFromJni
The C/C + + header file generated in this article is named: com_sundy_jnidemo_ChangeMethodFromJni.h
Implementing local methods in C/C + +
After generating the C/C + + header file, you need to write the local method corresponding to the header file. Note: the first parameter of all local methods points to the JNIEnv structure. This structure is used to call JNI functions. The meaning of the second parameter jclass depends on whether the method is static or Instance. In the former, jclass represents the reference of a class object, while the latter is the reference of the object to which the called method belongs.
The return value and parameter types are mapped to local C/C + + types according to the equivalence convention, as shown in table JNI type mapping. Some types can be used directly in local code, while others can only call operations through JNI.
Table A
Java type | Local type | describe |
boolean | jboolean | C/C++8-bit integer |
byte | jbyte | C/C + + signed 8-bit integer |
char | jchar | C/C + + unsigned 16 bit integer |
short | jshort | C/C + + signed 16 bit integer |
int | jint | C/C + + signed 32-bit integer |
long | jlong | C/C + + signed 64 bit integer e |
float | jfloat | C/C++32-bit floating point |
double | jdouble | C/C++64 bit floating point |
Object | jobject | Any Java object, or no object of corresponding Java type |
Class | jclass | Class object |
String | jstring | String object |
Object[] | jobjectArray | Array of any object |
boolean[] | jbooleanArray | Boolean array |
byte[] | jbyteArray | Bit array |
char[] | jcharArray | Character array |
short[] | jshortArray | Short integer array |
int[] | jintArray | integer array |
long[] | jlongArray | Long integer array |
float[] | jfloatArray | Floating point array |
double[] | jdoubleArray | Double floating point array |
※ JNI type mapping
Use array:
JNI operates Java arrays through the functions provided by JNIEnv. It provides two functions: one is to operate on a simple array of Java, and the other is to operate on an array of object types.
For speed reasons, arrays of simple types are exposed to local code as pointers to local types. Therefore, they can be accessed as regular arrays. This pointer is a pointer to the actual Java array or a copy of the Java array. In addition, the array is arranged to match the local type.
In order to access arrays of java simple types, you need to use the GetXXXArrayElements function (see table B). XXX represents the type of array. This function takes the Java array as a parameter and returns a pointer to the array of the corresponding local type.
Table B
function | Java Array type | Local type |
GetBooleanArrayElements | jbooleanArray | jboolean |
GetByteArrayElements | jbyteArray | jbyte |
GetCharArrayElements | jcharArray | jchar |
GetShortArrayElements | jshortArray | jshort |
GetIntArrayElements | jintArray | jint |
GetLongArrayElements | jlongArray | jlong |
GetFloatArrayElements | jfloatArray | jfloat |
GetDoubleArrayElements | jdoubleArray | jdouble |
※ JNI array access function
After you access the array, make sure to call the corresponding ReleaseXXXArrayElements function. The parameters are the pointers returned by the corresponding Java array and GetXXXArrayElements. If necessary, this release function will copy any changes you make (so that they are reflected into the Java array), and then release all related resources.
In order to use the array of java objects, you must use the GetObjectArrayElement function and SetObjectArrayElement function to get and set the elements of the array respectively. The GetArrayLength function returns the length of the array.
Use object
Another feature provided by JNI is the use of Java objects in native code. By using appropriate JNI functions, you can create Java objects, get, set static and instance fields, and call static and instance functions. JNI identifies domains and methods by ID. the ID of a domain or method is a necessary parameter of any function dealing with domains and methods.
Table C lists the JNI functions used to get the fields and methods of static and instance. Each function accepts (as a parameter) classes of fields or methods, their names, symbols, and their corresponding returned jfield ID or jmethodids.
Table C
function | describe |
GetFieldID | Get the domain ID of an instance |
GetStaticFieldID | Get the ID of a static domain |
GetMethodID | Get the ID of the method of an instance |
GetStaticMethodID | Get the ID of a static method |
※ functions of fields and methods
If you have an instance of a class, it can be obtained through the method GetObjectClass, or if you do not have an instance of this class, it can be obtained through FindClass. The symbol is a string returned from the field type or method parameter, as shown in table D.
Table D
Java type | Symbol |
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | L |
float | F |
double | D |
void | V |
objects object | Lfully-qualified-class-name;L class name |
Arrays array | [array type] |
methods method | (argument types) return type |
※ symbols for determining fields and methods
Let's take a look at how to use arrays and objects to obtain DiskInfo class objects in Java from in C + + and return a DiskInfo array:
//Returns a structure array and a structure array of hard disk information JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStructArray (JNIEnv *env, jobject _obj) { //Declare an object array jobjectArray args = 0; //Array size jsize len = 5; //Get the class to which the object belongs, usually ava/lang/Object jclass objClass = (env)->FindClass("java/lang/Object"); //New object array args = (env)->NewObjectArray(len, objClass, 0); /* The following is to get the variables in the corresponding instance class in Java*/ //Get instance classes in Java jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo"); //Get the definition of each variable in the class //name jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;"); //serial number jfieldID ival = (env)->GetFieldID(objectClass,"serial","I"); //Pay values to the variables of each instance, and add the instance as an object to the object array for(int i=0; i < len; i++ ) { //Value the variables of each instance jstring jstr = WindowsTojstring(env,"My disk name is D:"); //(env)->SetObjectField(_obj,str,(env)->NewStringUTF("my name is D:")); (env)->SetObjectField(_obj,str,jstr); (env)->SetShortField(_obj,ival,10); //Add to objcet array (env)->SetObjectArrayElement(args, i, _obj); } //Return object array return args; }
The implementation codes of all C/C + + methods are as follows:
// JniManage.cpp: defines the entry point for the DLL application. // package com.sundy.jnidemo; #include "stdafx.h" #include <stdio.h> #include <math.h> #include "jni.h" #include "jni_md.h" #include "./head/Base.h" #include "head/wmi.h" #include "head/com_sundy_jnidemo_ChangeMethodFromJni.h" / / generated through javah – jni javactransfer #include <stdio.h> #include "stdlib.h" #include "string.h" #pragma comment (lib,"BaseInfo.lib") #pragma comment (lib,"jvm.lib") //Hard disk information struct { char name[256]; int serial; }DiskInfo; /*BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { LPTSTR strName = new CHAR[256] ; (*GetHostName)(strName); printf("%s\n",strName); delete [] strName; return TRUE; }*/ //Convert jstring type to windows type char* jstringToWindows( JNIEnv *env, jstring jstr ); //Convert windows type to jstring type jstring WindowsTojstring( JNIEnv* env, char* str ); //Main function BOOL WINAPI DllMain(HANDLE hHandle, DWORD dwReason, LPVOID lpReserved) { return TRUE; } //Enter commonly used numeric types: Boolean,Byte,Char,Short,Int,Float,Double JNIEXPORT void JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_displayParms (JNIEnv *env, jobject obj, jstring s, jint i, jboolean b) { const char* szStr = (env)->GetStringUTFChars(s, 0 ); printf( "String = [%s]\n", szStr ); printf( "int = %d\n", i ); printf( "boolean = %s\n", (b==JNI_TRUE ? "true" : "false") ); (env)->ReleaseStringUTFChars(s, szStr ); } //Call a static method with only one simple type output JNIEXPORT jint JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_add (JNIEnv *env, jobject, jint a, jint b) { int rtn = (int)(a + b); return (jint)rtn; } Enter an array,What you enter here is a Boolean Array of type JNIEXPORT void JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_setArray (JNIEnv *env, jobject, jbooleanArray ba) { jboolean* pba = (env)->GetBooleanArrayElements(ba, 0 ); jsize len = (env)->GetArrayLength(ba); int i=0; // change even array elements for( i=0; i < len; i+=2 ) { pba[i] = JNI_FALSE; printf( "boolean = %s\n", (pba[i]==JNI_TRUE ? "true" : "false") ); } (env)->ReleaseBooleanArrayElements(ba, pba, 0 ); } Returns an array of strings JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStringArray (JNIEnv *env, jobject) { jstring str; jobjectArray args = 0; jsize len = 5; char* sa[] = { "Hello,", "world!", "JNI", "is", "fun" }; int i=0; args = (env)->NewObjectArray(len,(env)->FindClass("java/lang/String"),0); for( i=0; i < len; i++ ) { str = (env)->NewStringUTF(sa[i] ); (env)->SetObjectArrayElement(args, i, str); } return args; } //Returns a structure, where a simple structure type of hard disk information is returned JNIEXPORT jobject JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStruct (JNIEnv *env, jobject obj) { /* The following is to get the variables in the corresponding instance class in Java*/ //Get instance classes in Java jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo"); //Get the definition of each variable in the class //name jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;"); //serial number jfieldID ival = (env)->GetFieldID(objectClass,"serial","I"); //Value the variables of each instance (env)->SetObjectField(obj,str,(env)->NewStringUTF("my name is D:")); (env)->SetShortField(obj,ival,10); return obj; } //Returns a structure array and a structure array of hard disk information JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStructArray (JNIEnv *env, jobject _obj) { //Declare an object array jobjectArray args = 0; //Array size jsize len = 5; //Get the class to which the object belongs, usually ava/lang/Object jclass objClass = (env)->FindClass("java/lang/Object"); //New object array args = (env)->NewObjectArray(len, objClass, 0); /* The following is to get the variables in the corresponding instance class in Java*/ //Get instance classes in Java jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo"); //Get the definition of each variable in the class //name jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;"); //serial number jfieldID ival = (env)->GetFieldID(objectClass,"serial","I"); //Pay values to the variables of each instance, and add the instance as an object to the object array for(int i=0; i < len; i++ ) { //Value the variables of each instance jstring jstr = WindowsTojstring(env,"My disk name is D:"); //(env)->SetObjectField(_obj,str,(env)->NewStringUTF("my name is D:")); (env)->SetObjectField(_obj,str,jstr); (env)->SetShortField(_obj,ival,10); //Add to objcet array (env)->SetObjectArrayElement(args, i, _obj); } //Return object array return args; } //Convert jstring type to windows type char* jstringToWindows( JNIEnv *env, jstring jstr ) { int length = (env)->GetStringLength(jstr ); const jchar* jcstr = (env)->GetStringChars(jstr, 0 ); char* rtn = (char*)malloc( length*2+1 ); int size = 0; size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL ); if( size <= 0 ) return NULL; (env)->ReleaseStringChars(jstr, jcstr ); rtn[size] = 0; return rtn; } //Convert windows type to jstring type jstring WindowsTojstring( JNIEnv* env, char* str ) { jstring rtn = 0; int slen = strlen(str); unsigned short * buffer = 0; if( slen == 0 ) rtn = (env)->NewStringUTF(str ); else { int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 ); buffer = (unsigned short *)malloc( length*2 + 1 ); if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 ) rtn = (env)->NewString( (jchar*)buffer, length ); } if( buffer ) free( buffer ); return rtn; }
Java test native code
There's nothing more to say. Look at the code
//Main test procedure public static void main(String[] args) { ChangeMethodFromJni changeJni = new ChangeMethodFromJni(); //Enter the commonly used numeric type (string int boolean) System.out .println("------------------Enter common numeric types(string int boolean)-----------"); changeJni.displayParms("Hello World!", 100, true); //Call a static method System.out.println("------------------Call a static method-----------"); int ret = changeJni.add(12, 20); System.out.println("The result is: " + String.valueOf(ret)); //Enter an array System.out.println("------------------Enter an array-----------"); boolean[] blList = new boolean[] { true, false, true }; changeJni.setArray(blList); //Returns an array of strings System.out.println("------------------Returns an array of strings-----------"); String[] strList = changeJni.getStringArray(); for (int i = 0; i < strList.length; i++) { System.out.print(strList[i]); } System.out.println(); System.out.println("------------------Returns a structure-----------"); //Returns a structure DiskInfo disk = changeJni.getStruct(); System.out.println("name:" + disk.name); System.out.println("Serial:" + disk.serial); //Returns an array of structures System.out.println("------------------Returns an array of structures -----------"); DiskInfo[] diskList = changeJni.getStructArray(); for (int i = 0; i < diskList.length; i++) { System.out.println("name:" + diskList[i].name); System.out.println("Serial:" + diskList[i].serial); } }