The perfect combination of Java and C + + through JNI

Source: Internet
Author: User
Tags pprint

See: The course "Jni:java and C + +" in the high Longoza hall 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

First, the basic introduction

1. What is JNI?

Java Native Interface (Java Native Interface (JNI)) is a native programming interface, which is part of the JDK, and JNI provides several APIs that implement Java and other communications (primarily c&c++).

2. What is the use of JNI?

The two most common applications of JNI are calls to C + + from Java programs, as well as Java code calls from C + + programs.

3. What kind of 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 files and header files: jni.h (c header file), Jvm.lib and Jvm.dll (under Windows) or libjvm.so (under Linux).

(2) C and C + + compilers capable of creating dynamic-link libraries

The most common two C compilers are Visual C + + for Windows and GCC/CC for unit-based systems.


Ii. the perfect way for Java to invoke C + + code

JNI is a bridge between Java and C + +, and the hierarchical relationships between them are as follows:

The JNI layer is implemented in C, which logically belongs to the Java class.

C and C + + syntax is common, so in theory you can put the JNI (c-layer) code and the C + + layer code can be placed in the same document.

1. The principle of maintaining the stability of the JNI layer: "Static to static, dynamic to dynamic"

The JNI layer can either create Java layer objects or C + + layer objects. It is important to note that the global or static variables of the JNI layer (layer C) are only suitable for storing static data, such as Methodid or FieldID. Storing dynamic Java or C + + object references to global variables in JNI (layer C) can lead to instability in the JNI layer (layer C).

So: the principle of static to static is that the global variable or static variable of the JNI layer can only store the static data of the Java layer or C + + layer.

The principle of dynamic-to-dynamic is that objects created dynamically by the JNI layer can only be stored in dynamically created objects in the Java layer or C + + layer.

2. The following example shows how to store C + + objects dynamically created by the JNI layer at the Java layer.

First: The requirement for this example is to use classes in Java that have already been implemented in C + +.

The code for the C + + layer is as follows:

#pragma onceclass cfood {private:char* name;double price;public:cfood (char* name, double price) {this->name = Name;thi S->price = Price;} ~cfood () {if (name = null) {free (name); name = null;}} Const char* GetName () {return this->name;} Double GetPrice () {return this->price;}};


Java layer in order to use the above code, introduce a new class of food, as follows:

public class Food {static {system.loadlibrary ("Jnifood");} Object pointer to store C + + layer private int mobject;public food (String name, double price) {Setfoodparam (name, price);} public native void Setfoodparam (string name, double price);p ublic native String GetName ();p ublic native double GetPrice (); protected native void Finalize ();p ublic 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 ()));}}


Where the local method is declared, it is important to note that an int field is used to hold a pointer to the C + + Layer object. It is also important to be aware that a C + + object is refactored by a local method Finalize ().

Here is the implementation code for the JNI layer:

Header file:

#include <jni.h>/* Header for class Test2_food */#ifndef _included_test2_food#define _included_test2_food#ifdef _ _cplusplusextern "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_tes T2_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->New Stringutf ("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- &Gt 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 = en V->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 Cha r* tempname = env->getstringutfchars (name, 0); char* tempname = jstring2string (env, name);d ouble 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 CH ar* 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);}}


Third, the perfect combination of Java (Eclipse) and C + + (Visual Studio)

Place the debug version of the DLL under the Java project, set the breakpoint in eclipse with the local method, start Debug debugging, and set in the VS This DLL project: Debug->attach to Process Select Javaw.exe and then click "Attach".

This allows the local method to jump directly to the VS environment for Trace debugging, and after the local method has been debugged (press F5 in VS), go to Eclipse and continue debugging.


Iv. callback Java methods in C + + (not perfect)

As mentioned above, saving C + + objects in Java is perfect, without any side effects. But storing Java objects in C + + is a bit of a hassle. According to my experimental test, the Jobject type is not reliable, and it is very dangerous to use it to save Java objects in C + +. Here is my experimental data: (Full test code uploaded: http://download.csdn.net/detail/xiaoxiaoyusheng2012/9766376)

----------------------Java Wrapper calls the local method 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 <1>: EVN = 0x004ba514 Thiz = 0x0346fc6c Clazz = 0x03508a30//init sequence number <1>: EVN = 0x004ba514 Thiz = 0x03 46fc6c clazz = 0x03508a34//Register sequence <1>: EVN = 0x004ba514 Thiz = 0x0346fc9c Clazz = 0x03508a30//doprint serial number <1>: EVN = 0x004ba514 Thiz = 0x0346fc9c Clazz = 0x03508a30//doprint serial number <1>: EVN = 0x004ba514 Thiz = 0x0 346fc9c clazz = 0x03508a30//Doprint call local method relationships directly------------------------java: 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!");} Sequence <1>: EVN = 0x004fa514 Thiz = 0x034ffc6c Clazz = 0x03598a30//init sequence number <2>: EVN = 0x004fa514 Thiz = 0x0 34ffc9c clazz = 0x03598a34//Register sequence <3>: EVN = 0x004fa514 Thiz = 0x034ffc9c Clazz = 0x03598a30//Doprint Serial <4>: EVN = 0x004fa514 Thiz = 0x034ffc9c Clazz = 0x03598a30//doprint serial number <5>: EVN = 0x004fa514 Thiz = 0x034ffc9c clazz = 0x03598a30//Doprint

The result is: The value of Jobject will change, can not be saved in C + + code, Jobject value change reason, I guess with Java garbage collection mechanism. The JVM constantly collates memory, causing changes in the memory movement of Java objects. Therefore, many articles on the Internet jobject can be used directly, there should be a lot of problems.

If so, how C + + should callback Java code, my approach is to "always use the jnienv * and Jobject" in the Jni interface parameters to be used without saving. Here 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 ();p ublic native void Registerprint (Myprint myprint);p ublic native void Doprint (String text);p Rote cted native Void Finalize ();p ublic 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 __cplusplusextern "C" {#endif */* Class:     test1_myfile * Method:    init * Signature: () V */jniexport void Jnica LL 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 */jnie XPORT 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->GetFiel Did (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-& Gt 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, t Hiz, pFile);} Jniexport void Jnicall java_test1_myfile_registerprint (jnienv *env, Jobject thiz, Jobject jobj) {CMyPrint* pPrint = new C Myprint (); cmyfile* pFile = Getmyfile (env, thiz);p file->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);p file->doprint (env, Thiz, ptext); if (ptext! = NULL) {free (ptext) ;p text = NULL;}} Jniexport void Jnicall java_test1_myfile_finalize (jnienv *env, Jobject thiz) {cmyfile* pFile = getmyfile (env, thiz); if (p File = 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 Jobject as follows to get the Java object, then callback void OnPrint (JNIEnv *env, Jobject thiz, char* text) {Jclass clazz = Env->getobjec Tclass (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 PP Rint;pprint = NULL;}} void Registerprint (cmyprint* pprint) {this->pprint = Pprint;} void Doprint (jnienv *env1, Jobject thiz, char* text) {Pprint->onprint (ENV1, thiz, text);}};


V. Appendices

1. How to view the Java method signature:

CMD jumps to the directory where the. class file is located and executes javap-s-P XXX. (where xxx is the class name).



The perfect combination of Java and C + + through JNI

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.