Develop JNI applications with Delphi (transfer)

xiaoxiao2021-03-06  47

JNI (Java Native Interface, Java Local Interface) Technology is not unfamiliar, it can help solve the limitations and efficiency of Java access to the underlying hardware. With regard to the development of JNI, how many data discusses how to develop JNI with C / C language, and even JDK also provides a JavaH tool to automatically generate a C language program framework. However, do you have to communicate with Java with Java with your favorite Delphi?

Through the analysis of JNI.h files generated by Javah, we found that Java uses JNI to access the local code key in jninativeInterface_ this structure defined in jni.h, if you use DELHPI language to rewrite Its definition should also develop JNI local code. Fortunately, there is ready-made code on the Internet to help you complete this complicated job, providing a jni.pas file on http://delphi-jedi.org, is a JNI.h rewritten with Delphi language. We only need to join JNI.PAS in our Delphi project, you can easily develop a Delphi language-based JNI local code.

This article will use jni.pas to discuss the basic method of developing JNI local code with Delphi language.

Let's first look at a classic HelloWorld example. Write the following Java code:

Class HelloWorld {Public Native Void DisplayHelloWorld (); static {system.loadLibrary ("HelloWorldImpl");}}

This code declares a local method DisplayHelloWorld, it doesn't have a parameter, and there is no return value, but I hope it can print "Hello! China." On the screen. This task we intend to hand it over to the local Delphi. At the same time, in the static domain of this class, load HelloWorldImpl.dll with the System.LoadLibrary () method. Note that only the file name is required without the need to give the extension DLL.

At this time, if you use the HelloWorld class of the HelloWorld class in our Java program, the system will throw a java.lang.unsatisfiedLinkerRror, because we have not implemented local code for it.

Let's take a look at the implementation of the local code in Delphi. Newly built a DLL project, the project is called HelloWorldImpl, enter the following code:

Usesjni; procedure java_helloWorld_DisplayHelloWorld (Penv: PjNienv; Obj: jobject); stdcall; beginwriteln ('Hello! China.'); End; exportsjava_helloworld_displayhelloworld; end.

This code first imports jni.pas unit. Then there is a process called java_helloWorld_DisplayHelloWorld, which is naming in this process. It starts with Java, using the underline to connect the Java class's package, class name, and method. This naming method cannot be incorrect, otherwise the Java class will not be able to correspond to it with it. At the same time, on the Win32 platform, the call mode of this process can only be declared as stdcall. Although there is no parameters declared in the HelloWorld class, the specific processes implemented in Delphi have two parameters: Penv: Pjnienv and Obj: Jobject. (These two types are defined in jni.pas). The PENV parameter represents the JVM environment, while the OBJ parameter represents the Java object that calls this process. Of course, these two parameters will not be used in our simple example. Because we compile a DLL file, this method needs to be output in Exports. Compile Delphi Engineering, generate a HelloWorldimp.dll file, placed on the runtime system to find the directory, usually in the current directory, and write the Java class that calls the HelloWorld class as follows:

Class maintest {public static void main (String [] args) {new helloworld (). DisplayHelloWorld ();}}

Run it, if the console outputs "Hello! China.", Congratulations, you have successfully developed the first JNI application with Delphi.

Next, we will raise a little bit to study the passage of the parameters. Or HelloWorld, modify the displayhelloworld method just written, let the displayed string dynamically determine by the Java class. The Java code for the new DisplayHelloWorld method is as follows:

Public Native Void DisplayHelloWorld (String Str);

Modify the Delphi code, this time is used up to the first inherent parameter PENV of the process, as follows:

procedure Java_HelloWorld_displayHelloWorld (PEnv: PJNIEnv; Obj: JObject; str: JString); stdcall; varJVM: TJNIEnv; beginJVM: = TJNIEnv.Create (PEnv); Writeln (JVM.UnicodeJStringToString (str)); JVM.Free; end;

In the parameter table of the process, we add a parameter str: JString, which is responsible for receiving Striang arguments from HelloWorld. Note that the implementation of the code is different because the parameters are used, which involves the conversion between the data types of the parameters. The String object of Java from the Java program is now a special jstring type, and JString is not available in Delphi. The unicodejstringtostring () method provided by Tjnienv is required to convert the String type that Delphi can recognize. Therefore, it is necessary to construct the example object of Tjnienv. Use it (Tjnienv provides a number of methods, here only one of its most commonly used methods), and finally, remember to release it. For the parameters of the basic data type, the steps you use from Java to Delphi and used in Delphi are as simple.

We will increase a little difficulty, build a custom class book, and pass it as a parameter into Delphi, study how the public fields of the object parameters in the local code are studied. First, define a simple Java class book, in order to make the problem slightly more complicated, we add a java.util.date type field in Book, the code is as follows:

Public class book {public string title; // Title public double price; // Price public date pdate; // Purchase Date}

Similarly, add a local method DisplayBookInfo in the HelloWorld class, the code is as follows:

public native void displayBookInfo (Book b); Delphi code with respect to the above examples, a bit too complicated, take a look at the code: procedure Java_HelloWorld_displayBookInfo (PEnv: PJNIEnv; Obj: JObject; b: JObject); stdcall; varJVM : Tjnienv; c, c2: jclass; FID: JMELDID; MID: JMETHODID; TITLE, DATESTR: STRING; Price: Double; PDATE: JOBJECT; beginjvm: = tjnienv.create (PENV); c: = jvm.getObjectClass (b) ; FID: = JVM.GetfieldID (C, 'Title', 'Ljava / Lang / String;'); title: = JVM.UnicodejStringTString (JVM.GetObjectfield (B, FID)); FID: = JVM.GetfieldID (C, 'price', 'd'); Price: = jvm.getdoublefield (B, FID); FID: = JVM.GetfieldID (C, 'PDATE', 'LJAVA / UTIL / date;'); pdate: = jvm.getObjectField (B, FID); C2: = JVM.GetObjectClass (pdate); MID: = JVM.GetMethodId (C2, 'TOString', '() Ljava / Lang / String;'); DateStr: = JVM.JSTRINGTOSTRING (JVM. CallObjectMethoda (PDATE, MID, NIL); WRITELN (Format ('% S% F% S', [Title, Price, DateStr]); JVM.Free; end;

Parameter B: Jobject is the incoming book object. The getObjectClass method will be adjusted, obtain the class C to which it belongs is obtained according to the B object, and then call the GetFieldID method to get a field ID called Title attribute, must pass the correct type signature. Then you can get the value of the field from the object based on the obtained field ID through the getObjectField method. Note the order here: We get the incoming object parameters (Object), you must first get its class (Class), so that both object instances, there is a class, after getting a field ID from the class, according to field The ID gets a field value from the object. For a static field of the class, you can get its values ​​directly from the class without passing the object. If you want to call the method of the object, the operation step is basically similar, and it is also necessary to obtain the method ID in the class, and the corresponding method of the object is executed. In this example, because we add a java.util.date type field, to access such a field, you can only use it as Jobject read, and further access its members in the same method (attribute Or method). This example demonstrates how to access the members of the Date object toString. To properly access the member properties (fields) and members of the class, the most important thing is to give the correct signature, and the signature of the data type and method in Java has the following agreement:

Through the example above, we understand the basic steps of the member attributes or methods of accessing the object parameters and the use of multiple GET methods. Tjnienv also provides a plurality of SET methods to modify the field values ​​of the incoming object parameters because the Java object parameters are transmitted in the way of address, so the modified results can be reflected in the Java program. The Get / SET method provided by Tjnienv requires two basic parameters: object instance (Jobject Type), and field ID (JFIELD type), you can get the value of this object in accordance with the provided objects and field IDs.

Now we understand the steps of using and modifying the Java object in Delphi code. Further, if you need to create a new Java object from the Delphi, can you? Let's take an example, create an instance of the Java class in Delphi, and the operation method is also very simple.

First add a local method in the Java code, as follows:

Public Native Book Findbook (String T);

Then, modify the Delphi code, add a function (because there is a return value, so no longer the process is functional):

function Java_HelloWorld_findBook (PEnv: PJNIEnv; Obj: JObject; t: JString): JObject; stdcall; varJVM: TJNIEnv; c: JClass; fid: JFieldID; b: JObject; mid: JMethodID; beginJVM: = TJNIEnv.Create (PEnv); C: = JVM.FindClass ('Book'); MID: = JVM.GetMethodId (C, '', '() V'); B: = JVM.NewObjectv (C, MID, NIL); FID: = JVM.GetfieldID (C, 'Title', 'Ljava / Lang / String;'); JVM.SetObjectField (B, FID, T); FID: = JVM.GetfieldID (C, 'Price', 'D'); JVM.SETDOUBLEELD (B, FID, 99.8); Result: = B; JVM.Free; End; first use the FindClass method to find the class according to the class name, then obtain the method ID of the constructor, the constructor name is fixed to " ", Pay attention to signing" () v "Description Use an empty constructor of the Book class. Then use the method NewObjectv to create an instance of the class according to the method ID of the class and constructor. Create a class instance, and then operate with the previous example, there is no different example. For non-empty constructors, it is slightly more complicated. It is necessary to set its parameter table. Or the above example, add a non-air constructor in the Book class:

Public book (strint t, double p) {this.title = T; this.price = p;}

In the Delphi code, the code of the FindBook function modifies the acquisition method ID is as follows:

MID: = JVM.getMethodId (C, '', '(ljava / lang / string; d) v');

The constructor is still "", the method signature indicates that it has two parameters, respectively, String and Double. Then the incoming parameters, the method of calling the Java object in Delphi If you need to pass parameters, you need to construct a parameter array. Plus in variable declarations:

Args: array [0..1] of jvalue;

note! The parameters are the JVALUE type, whether it is the basic data type or an object, all as an array of JValue. In the code implementation is the parameter setting value, and the address of the array is passed to the NewObjecta method as the parameters:

Args [0] .l: = t; // t is incoming jstring parameter args [1] .d: = 9.8; B: = JVM.NewObjecta (C, MID, @ args);

The statement of the data setting value for the JVALUE type is a bit special, is it? We open jni.pas, check out JValue's definition, which is a Packed Record, which already includes a variety of data types, and Jvalue is as follows:

Jvalue = Packed Recordcase Integer OF0: (Z: JBOOLEAN); 1: (B: JBYTE); 2: (C: JCHAR); 3: (s: jshort); 4: (i: jint); 5: (J: Jlong); 6: (f: jflloat); 7: (d: jdouble); 8: (l: jobject);

Let's take a look at the error handling. In the example in debugging, you may see that once an error occurs during the execution of Delphi, the console will output a lot of error messages. If you want to block this information, that is, I hope to capture errors in Delphi and process it directly. What should I do? It is also very simple, providing two methods in Tjnienv can easily handle errors that occur when accessing Java objects. Var ... ae: jthrowable; begin ... ae: = jvm.exceptionoccurred; if (ae <> nil).. . EXCEPTIONDESCRIBE; JVM.Exceptionclear; End; ...

Method EXCEPTIONOCCURRED can capture the error thrown by Java and store it in a variable of the JTHROWABLE type. With ExceptionDescribe, you can display the Java error message, and ExceptionClear is clearly clearing the error, making it no longer thrown.

At this point, we have made a preliminary discussion from the step from Java code to access Delphi local code from JNI technology. In JNI.PAS, it also provides a method of opening a Java virtual machine in Delphi to perform Java code. Interested readers may wish to study themselves.

转载请注明原文地址:https://www.9cbs.com/read-80013.html

New Post(0)