See: Gao Huantang's course JNI: the beautiful combination of Java and C + + http://edu.csdn.net/course/detail/1469
see: http://www.cnblogs.com/yejg1212/archive/2013/06/07/3125392.html see: http://blog.csdn.net/jiangwei0910410003/article/details/17465457
1, Basic introduction
1. What is JNI?
Java Native interface( Java Native Interface (JNI))Is a native programming interface, which is JDK Part of it, JNI It provides several API,And Java And other communications (mainly C&C++).
2. What's the use of JNI?
JNI The two most common applications:from Java Program call C/C++,And from C/C++Program call Java code.
3. What environment is required to use JNI?
(1),JDK Tools and components:(Java compiler: javac.exe ,JVM : java.exe ,Local method C File generator: javah.exe) Library and header files: jni.h( C Header file),jvm.lib and jvm.dll(windows lower) or libjvm.so(linux lower). (2),Ability to create dynamic link libraries C and C++compiler The two most common C The compiler is used for Windows of Visual C++ And for UNIT Systematic gcc/cc.
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. The former represents a reference to a class object, while the latter is a reference to the object to which the called method belongs.
Common functions:
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.
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 the array. This function takes the Java array as a parameter and returns a pointer to the array of the corresponding local type.
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 fields and methods through ID. the ID of a field or method is a necessary parameter of any function dealing with fields and methods.
Table C lists the JNI functions used to get static and instance fields and methods. Each function accepts (as a parameter) the classes of fields or methods, their names, symbols, and their corresponding returned jfield ID or jmethodids.
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.
2, The perfect way for Java to call C + + code
JNI yes Java And C++The hierarchical relationship between the bridges is shown in the figure below:
JNI layer is implemented in C mode and logically belongs to Java class.
The syntax of C and C + + is common, so in theory, JNI(C-layer) code and C + + layer code can be placed in the same document.
1. The principle of maintaining the stability of JNI layer: "static to static, dynamic to dynamic"
JNI Layers can be created Java Layer objects, or C++Layer objects. Special attention should be paid to: JNI Layers( C Global or static for layer)( static)Variables are only suitable for storing static data, such as methodID or fieldID Wait. Put dynamic Java or C++Object references are stored in JNI(C layer)Global variables that cause JNI Layers( C Layer) instability. Therefore, the principle of "static to static" is: JNI A layer's global or static variables can only be stored Java Layer or C++Layer static data. "The principle of "dynamic to dynamic" is: JNI Objects created dynamically by layers can only be stored in Java Layer or C++Dynamically created objects in layers.
2. The following example shows how to store C + + objects dynamically created by the JNI layer in the Java layer.
First: the requirement of this example is to use the classes already implemented in C + + in Java.
The code of C + + layer is as follows:
#pragma once class CFood { private: char* name; double price; public: CFood(char* name, double price) { this->name = name; this->price = price; } ~CFood() { if(name != NULL) { free(name); name = NULL; } } const char* getName() { return this->name; } double getPrice() { return this->price; } };
In order to use the above code, the Java layer introduces a new class Food, as follows:
public class Food { static { System.loadLibrary("jniFood"); } // Object pointers for storing C + + layers private int mObject; public Food(String name, double price) { setFoodParam(name, price); } public native void setFoodParam(String name, double price); public native String getName(); public native double getPrice(); protected native void finalize(); public static void main(String[] args) { Food f1 = new Food("bread", 1.99); Food f2 = new Food("milk", 3.99); System.out.println(String.format("Food:%s, Unit Price:%f", f1.getName(), f1.getPrice())); System.out.println(String.format("Food:%s, Unit Price:%f", f2.getName(), f2.getPrice())); } }
Among them, the local method is declared. It should be noted that an int field is created to store the pointer of the C + + layer object. In addition, it should be noted that C + + objects are destructed through the local method finalize().
The following is the implementation code of JNI layer:
Header file:
#include <jni.h> /* Header for class test2_Food */ #ifndef _Included_test2_Food #define _Included_test2_Food #ifdef __cplusplus extern "C" { #endif /* * Class: test2_Food * Method: setFoodParam * Signature: (Ljava/lang/String;D)V */ JNIEXPORT void JNICALL Java_test2_Food_setFoodParam (JNIEnv *, jobject, jstring, jdouble); /* * Class: test2_Food * Method: getName * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_test2_Food_getName (JNIEnv *, jobject); /* * Class: test2_Food * Method: getPrice * Signature: ()D */ JNIEXPORT jdouble JNICALL Java_test2_Food_getPrice (JNIEnv *, jobject); /* * Class: test2_Food * Method: finalize * Signature: ()V */ JNIEXPORT void JNICALL Java_test2_Food_finalize (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
Source file:
#include "stdafx.h" #include <stdlib.h> #include "test2_Food.h" #include "Food.h" char* jstring2string(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("utf-8"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*)malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; } jstring char2Jstring(JNIEnv* env, const char* pat) { jclass strClass = env->FindClass("Ljava/lang/String;"); jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V"); jbyteArray bytes = env->NewByteArray(strlen(pat)); env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat); jstring encoding = env->NewStringUTF("utf-8"); return (jstring)env->NewObject(strClass, ctorID, bytes, encoding); } CFood* getCFood(JNIEnv *env, jobject thiz) { jclass clazz = env->GetObjectClass(thiz); jfieldID fid = env->GetFieldID(clazz, "mObject", "I"); jint p = env->GetIntField(thiz, fid); return (CFood*)p; } void setFood(JNIEnv *env, jobject thiz, const CFood* pFood) { jclass clazz = env->GetObjectClass(thiz); jfieldID fid = env->GetFieldID(clazz, "mObject", "I"); env->SetIntField(thiz, fid, (jint)pFood); } JNIEXPORT void JNICALL Java_test2_Food_setFoodParam (JNIEnv *env, jobject thiz, jstring name, jdouble price) { //const char* tempName = env->GetStringUTFChars(name, 0); char* tempName = jstring2string(env, name); double tempPrice = price; CFood* pFood = new CFood(tempName, tempPrice); setFood(env, thiz, pFood); } JNIEXPORT jstring JNICALL Java_test2_Food_getName (JNIEnv *env, jobject thiz) { CFood* pFood = getCFood(env, thiz); const char* name = pFood->getName(); return char2Jstring(env, name); } JNIEXPORT jdouble JNICALL Java_test2_Food_getPrice (JNIEnv *env, jobject thiz) { CFood* pFood = getCFood(env, thiz); return pFood->getPrice(); } JNIEXPORT void JNICALL Java_test2_Food_finalize (JNIEnv *env, jobject thiz) { CFood* pFood = getCFood(env, thiz); if (pFood != NULL) { delete pFood; pFood = NULL; setFood(env, thiz, pFood); } }
3, Perfect joint debugging of Java (Eclipse) and C++(Visual Studio)
take Debug Version dll Put on Java Under project, in Eclipse Set the breakpoint for the local method and start Debug Debugging, at the same time VS Should dll Settings in the project: Debug->Attach to Process ->choice javaw.exe Then click“ Attach". In this way, the local method can jump directly to VS Trace debugging in the environment, and after the local method debugging is completed (in the VS Middle press F5)Just go to Eclipse Continue debugging in.
4, Callback Java method in C + + (not perfect)
As mentioned above, in Java Save in C++The object is perfect without any side effects. But in C++Storage in Java Object, it's more troublesome. According to my experimental test jobject Type is not reliable, use it in C++Save in Java Objects have great hidden dangers. Here are my experimental data:(The complete test code is uploaded to:http://download.csdn.net/detail/xiaoxiaoyusheng2012/9766376)
---------------------- java Calling a local method in a wrapper public static void main(String[] args) { MyFile myFile = new MyFile(); MyPrint myPrint = new MyPrint(); //myFile.registerPrint(myPrint); myFile.setPrint(myPrint); myFile.setMyPrint(myPrint); myFile.doPrint("hello world!"); myFile.doPrint("again!"); myFile.doPrint("next!"); } ----------------------- Relationship: jclass clazz = env->GetObjectClass(thiz); Serial number<1> : evn = 0x004ba514 thiz = 0x0346fc6c clazz = 0x03508a30 // init Serial number<1> : evn = 0x004ba514 thiz = 0x0346fc6c clazz = 0x03508a34 // register Serial number<1> : evn = 0x004ba514 thiz = 0x0346fc9c clazz = 0x03508a30 // doPrint Serial number<1> : evn = 0x004ba514 thiz = 0x0346fc9c clazz = 0x03508a30 // doPrint Serial number<1> : evn = 0x004ba514 thiz = 0x0346fc9c clazz = 0x03508a30 // doPrint ------------------------java Call local methods directly in Relationship: jclass clazz = env->GetObjectClass(thiz); public static void main(String[] args) { MyFile myFile = new MyFile(); MyPrint myPrint = new MyPrint(); myFile.registerPrint(myPrint); myFile.setPrint(myPrint); //myFile.setMyPrint(myPrint); myFile.doPrint("hello world!"); myFile.doPrint("again!"); myFile.doPrint("next!"); } Serial number<1> : evn = 0x004fa514 thiz = 0x034ffc6c clazz = 0x03598a30 // init Serial number<2> : evn = 0x004fa514 thiz = 0x034ffc9c clazz = 0x03598a34 // register Serial number<3> : evn = 0x004fa514 thiz = 0x034ffc9c clazz = 0x03598a30 // doPrint Serial number<4> : evn = 0x004fa514 thiz = 0x034ffc9c clazz = 0x03598a30 // doPrint Serial number<5> : evn = 0x004fa514 thiz = 0x034ffc9c clazz = 0x03598a30 // doPrint 173
The above result is that the value of jobject will change and cannot be saved in C + + code. I guess the reason for the change of jobject value is related to Java's garbage collection mechanism. The JVM constantly cleans up memory, resulting in changes such as memory movement of Java objects. Therefore, many articles on the Internet say that jobproject can be used directly. There should be many problems.
If so, then C++How to callback java Code, my method is "always JNI In interface parameters JNIEnv * and jobject "It is used together with parameters and is not saved. The following is an implementation code:
Java layer code:
package test1; class MyPrint { public void onPrint(String text) { System.out.println(text); } } public class MyFile { private MyPrint myPrint = null; static { System.loadLibrary("jniTest5"); } private int mObject; public MyFile() { init(); } public void onPrint(String text) { myPrint.onPrint(text); } public void setPrint(MyPrint myPrint) { this.myPrint = myPrint; } public void setMyPrint(MyPrint myPrint) { setPrint(myPrint); this.registerPrint(myPrint); } public void myPrint(String text) { this.doPrint(text); } public native void init(); public native void registerPrint(MyPrint myPrint); public native void doPrint(String text); protected native void finalize(); public static void main(String[] args) { MyFile myFile = new MyFile(); MyPrint myPrint = new MyPrint(); myFile.setMyPrint(myPrint); myFile.doPrint("hello world!"); myFile.doPrint("again!"); myFile.doPrint("next!"); } }
JNI interface layer:
Header file: test1_MyFile.h
#include <jni.h> /* Header for class test1_MyFile */ #ifndef _Included_test1_MyFile #define _Included_test1_MyFile #ifdef __cplusplus extern "C" { #endif /* * Class: test1_MyFile * Method: init * Signature: ()V */ JNIEXPORT void JNICALL Java_test1_MyFile_init (JNIEnv *, jobject); /* * Class: test1_MyFile * Method: registerPrint * Signature: (Ltest1/MyPrint;)V */ JNIEXPORT void JNICALL Java_test1_MyFile_registerPrint (JNIEnv *, jobject, jobject); /* * Class: test1_MyFile * Method: doPrint * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_test1_MyFile_doPrint (JNIEnv *, jobject, jstring); /* * Class: test1_MyFile * Method: finalize * Signature: ()V */ JNIEXPORT void JNICALL Java_test1_MyFile_finalize (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
Source file:
#include "stdafx.h" #include <jni.h> #include "MyFile.h" #include "test1_MyFile.h" char* jstring2string(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("utf-8"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*)malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; } CMyFile* getMyFile(JNIEnv *env, jobject thiz) { jclass clazz = env->GetObjectClass(thiz); jfieldID fid = env->GetFieldID(clazz, "mObject", "I"); jint p = env->GetIntField(thiz, fid); return (CMyFile*)p; } void setMyFile(JNIEnv *env, jobject thiz, CMyFile* pFile) { jclass clazz = env->GetObjectClass(thiz); jfieldID fid = env->GetFieldID(clazz, "mObject", "I"); env->SetIntField(thiz, fid, (jint)pFile); } JNIEXPORT void JNICALL Java_test1_MyFile_init (JNIEnv *env, jobject thiz) { CMyFile* pFile = new CMyFile(); setMyFile(env, thiz, pFile); } JNIEXPORT void JNICALL Java_test1_MyFile_registerPrint (JNIEnv *env, jobject thiz, jobject jobj) { CMyPrint* pPrint = new CMyPrint(); CMyFile* pFile = getMyFile(env, thiz); pFile->registerPrint(pPrint); } JNIEXPORT void JNICALL Java_test1_MyFile_doPrint (JNIEnv *env, jobject thiz, jstring strText) { CMyFile* pFile = getMyFile(env, thiz); char* pText = jstring2string(env, strText); pFile->doPrint(env, thiz, pText); if (pText != NULL) { free(pText); pText = NULL; } } JNIEXPORT void JNICALL Java_test1_MyFile_finalize (JNIEnv *env, jobject thiz) { CMyFile* pFile = getMyFile(env, thiz); if (pFile != NULL) { delete pFile; pFile = NULL; setMyFile(env, thiz, pFile); } }
C + + layer:
// MyPrint.h
#include "stdafx.h" #include <jni.h> #include <stdlib.h> class CMyPrint { public: jstring char2Jstring(JNIEnv* env, const char* pat) { jclass strClass = env->FindClass("Ljava/lang/String;"); jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V"); jbyteArray bytes = env->NewByteArray(strlen(pat)); env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat); jstring encoding = env->NewStringUTF("utf-8"); return (jstring)env->NewObject(strClass, ctorID, bytes, encoding); } // Pass JNIEnv * and jobobject as follows to get the Java object, and then call back void onPrint(JNIEnv *env, jobject thiz, char* text) { jclass clazz = env->GetObjectClass(thiz); jmethodID methodID = env->GetMethodID(clazz, "onPrint", "(Ljava/lang/String;)V"); jstring strText = char2Jstring(env, text); env->CallVoidMethod(thiz, methodID, strText); } };
// MyFile.h
#pragma once #include "MyPrint.h" class CMyFile { private: CMyPrint* pPrint; public: ~CMyFile() { if (pPrint != NULL) { delete pPrint; pPrint = NULL; } } void registerPrint(CMyPrint* pPrint) { this->pPrint = pPrint; } void doPrint(JNIEnv *env1, jobject thiz, char* text) { pPrint->onPrint(env1, thiz, text); } };
5, Callback Java method in C + + (perfect)
The above callback is completed in a thread stack and cannot be called a true callback. In the follow-up learning practice, it is found that: JNIEnv *Only valid in the current thread, so JNIEnv*Should not be cached. What you can cache is JavaVM*. In addition, JNI The parameters of the interface in are local references. When the method stack is executed, the local references will be destroyed, so even if they need to be cached JavaVM*,Should also JavaVM*Convert to global reference and cache again. jobject You can also convert to a global post reference cache. The perfect callback method here is: we are C++Layer save Java Object. The specific approach is to jobject Cache after conversion to global reference.
The following is a test code:
Java layer:
package test1; class MyPrint { public void onPrint(String text) { System.out.println(text); } } public class MyFile { private MyPrint myPrint = null; static { System.loadLibrary("jniTest7"); } private int mObject; public MyFile() { init(); } public void setPrint(MyPrint myPrint) { this.myPrint = myPrint; } public void setMyPrint(MyPrint myPrint) { setPrint(myPrint); this.registerPrint(myPrint); } public native void init(); protected native void finalize(); public native void registerPrint(MyPrint myPrint); public native void doPrint(String text); public static void main(String[] args) { MyFile myFile = new MyFile(); MyPrint myPrint = new MyPrint(); myFile.setMyPrint(myPrint); myFile.doPrint("hello world!"); System.out.println("Waiting for print results..."); try { Thread.sleep(20*1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
JNI layer:
CMyFile* getMyFile(JNIEnv *env, jobject thiz) { jclass clazz = env->GetObjectClass(thiz); jfieldID fid = env->GetFieldID(clazz, "mObject", "I"); jint p = env->GetIntField(thiz, fid); return (CMyFile*)p; } void setMyFile(JNIEnv *env, jobject thiz, CMyFile* pFile) { jclass clazz = env->GetObjectClass(thiz); jfieldID fid = env->GetFieldID(clazz, "mObject", "I"); env->SetIntField(thiz, fid, (jint)pFile); } JNIEXPORT void JNICALL Java_test1_MyFile_init (JNIEnv *env, jobject thiz) { CMyFile* pFile = new CMyFile(); setMyFile(env, thiz, pFile); } JNIEXPORT void JNICALL Java_test1_MyFile_finalize (JNIEnv *env, jobject thiz) { CMyFile* pFile = getMyFile(env, thiz); if (pFile != NULL) { delete pFile; pFile = NULL; setMyFile(env, thiz, pFile); } } JNIEXPORT void JNICALL Java_test1_MyFile_registerPrint (JNIEnv *env, jobject thiz, jobject jPrint) { JavaVM* pVM = NULL; env->GetJavaVM(&pVM); // Generate global references from local references JavaVM* g_pVM = (JavaVM*)env->NewGlobalRef((jobject)pVM); jobject g_javaPrint = env->NewGlobalRef(jPrint); CMyPrint* pPrint = new CMyPrint(g_pVM, g_javaPrint); CMyFile* pFile = getMyFile(env, thiz); pFile->registerPrint(pPrint); } JNIEXPORT void JNICALL Java_test1_MyFile_doPrint (JNIEnv *env, jobject thiz, jstring strText) { CMyFile* pFile = getMyFile(env, thiz); char* pText = CMyPrint::jstring2string(env, strText); pFile->doPrint(pText); if (pText != NULL) { free(pText); pText = NULL; } }
C + + layer:
typedef struct _ThreadParam { JavaVM* jvm; jobject javaPrint; string text; }ThreadParam; DWORD WINAPI funproc(LPVOID lpparentet); class CMyPrint { private: jobject mJavaPrintObj; JavaVM* jvm; public: CMyPrint(JavaVM* jvm, jobject javaPrintObj) { this->jvm = jvm; this->mJavaPrintObj = javaPrintObj; } ~CMyPrint() { JNIEnv* pEnv = NULL; jvm->AttachCurrentThread((void**)&pEnv, NULL); pEnv->DeleteGlobalRef(mJavaPrintObj); pEnv->DeleteGlobalRef((jobject)jvm); } static char* jstring2string(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("utf-8"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*)malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; } static jstring char2Jstring(JNIEnv* env, const char* pat) { jclass strClass = env->FindClass("Ljava/lang/String;"); jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V"); jbyteArray bytes = env->NewByteArray(strlen(pat)); env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat); jstring encoding = env->NewStringUTF("utf-8"); return (jstring)env->NewObject(strClass, ctorID, bytes, encoding); } void onPrint(char* text) { ThreadParam* param = new ThreadParam(); param->jvm = jvm; param->javaPrint = mJavaPrintObj; param->text = text; HANDLE hander = CreateThread(NULL,0,funproc,param,0,NULL); } }; DWORD WINAPI funproc(LPVOID lpparentet) { Sleep(10*1000); ThreadParam* param = (ThreadParam*)lpparentet; JNIEnv* pEnv = NULL; param->jvm->AttachCurrentThread((void**)&pEnv, NULL); jclass clazz = pEnv->GetObjectClass(param->javaPrint); // Get non static method ID jmethodID methodID = pEnv->GetMethodID(clazz, "onPrint", "(Ljava/lang/String;)V"); jstring strText = CMyPrint::char2Jstring(pEnv, param->text.c_str()); // Call non static method pEnv->CallVoidMethod(param->javaPrint, methodID, strText); if (param != NULL) { delete param; param = NULL; } return 0; } class CMyFile { private: CMyPrint* pPrint; public: ~CMyFile() { if (pPrint != NULL) { delete pPrint; pPrint = NULL; } } void registerPrint(CMyPrint* pPrint) { this->pPrint = pPrint; } void doPrint(char* text) { pPrint->onPrint(text); } };
Operation results:
Waiting for print results... hello world! Vi JNI Perfect conversion of character encoding in see: http://www.cnblogs.com/westblade/p/4803968.html 1,Related concepts: (1),Java The layer used is 16 bit of unicode code(utf-16)To represent a string, both Chinese and English, are two bytes. (2),JNI Layer uses UTF-8 code, UTF-8 It's variable length coding unicode,commonly ascii Character 1 byte, Chinese 3 bytes. (3),C/C++Using raw data, ascii Is a byte, Chinese is generally GB2312 Encoding, using two bytes to represent a Chinese character. 2,Character flow direction (1),Java ---> C/C++ At this time, Java Call with UTF-16 Encoding, when the string is passed to JNI Method, C/C++The input obtained is jstring,At this time, JNI Two functions are provided, one is GetStringUTFChars,This function will get a UTF-8 Encoded string( char*Type), the other function is GetStringChars,This function will get a UTF-16 Encoded string( wchar_t*Type). No matter which result, if the obtained string contains Chinese, it needs to be further converted to GB2312 code. (2),C/C++ ---> Java At this time, yes JNI Return to Java character string. C/C++First, you should be responsible for turning this string into UTF-8 or UTF-16 Format, and then through NewStringUTF perhaps NewString To encapsulate it into jstring,Return to Java That's it. If the string does not contain Chinese characters, it is only standard ascii Code, then use GetStringUTFChars/NewStringUTF You can handle it, because in this case, UTF-8 Coding and ascii The coding is consistent and no conversion is required. If there are Chinese characters in the string, then C/C++Part must be coded. We need two conversion functions. One is to UTf-8/-16 Code conversion GB2312;The other is to GB2312 Turn into UTF-8/-16. Here we need to explain: linux and win32 All support wchar,This is actually 16 in width bit of unicode code UTF-16,So, if our c/c++Fully used in the program wchar Type, then this conversion is not required in theory. But in fact, we can't use it completely wchar To replace char Therefore, conversion is still necessary for most applications. (3),use wide char Type to convert ```cpp char* jstringToWindows( JNIEnv *env, jstring jstr ) { //Convert UTF8/16 to gb2312 int length = (env)->GetStringLength(jstr ); const jchar* jcstr = (env)->GetStringChars(jstr, 0 ); int clen = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, NULL,0, NULL, NULL ); char* rtn = (char*)malloc( clen ) //corrections. The author originally used (char *) malloc (length * 2 + 1). When the java string contains both Chinese characters and English letters, the required buffer size is not double. int size = 0; size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,clen, NULL, NULL ); if( size <= 0 ) return NULL; (env)->ReleaseStringChars(jstr, jcstr ); rtn[size] = 0; return rtn; } jstring WindowsTojstring( JNIEnv* env, const char* str ) {//Converting gb2312 to utf8/16 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; }
7, Appendix
1. How to view Java method signatures:
CMD Jump to .class File directory, execute javap -s -p XXX Just. (including: XXX Is the class name).