📄 delphi-jni-2.html
字号:
* Java declaration:
* native public void displayHelloWorld();
*
* The canonical first method.
*
* Class: Native
* Method: displayHelloWorld
* Signature: ()V
*)</i></font>
<b>procedure</b> Java_Native_displayHelloWorld(PEnv: PJNIEnv; Obj: JObject); <b>stdcall</b>;
<b>begin</b>
WriteLn(<font color="#9933CC">'Hello world!'</font>);
<b>end</b>;
</code></pre>
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>
<div class="SectionHeader">
<A NAME="Concept2">Concept #2 - Passing/Returning Simple Types</A>
</div>
<BLOCKQUOTE>
This is an example of a function that expects 2 integer parameters, multiplies them together, and returns the result.
<pre class="sourcecode"><code><font color="#003399"><i>(****************************************************************************
* Java declaration:
* native public void multiplyIntegers(int op1, int op2);
*
* Multiplies 2 integers and returns the result.
*
* Class: Native
* Method: multiplyIntegers
* Signature: (II)I
*)</i></font>
<b>function</b> Java_Native_multiplyIntegers(PEnv: PJNIEnv; Obj: JObject; Op1: JInt; Op2: JInt): JInt; <b>stdcall</b>;
<b>begin</b>
Result := Op1 * Op2;
<b>end</b>;
</code></pre>
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>
<div class="SectionHeader">
<A NAME="Concept3">Concept #3 - Passing an Array of Primitive Types to Delphi</A>
</div>
<BLOCKQUOTE>
This example shows how to pass a Java Array to a Delphi method. Each element of the array is set to a value (specified
as a parameter.) The native method <tt><b>initializeByteArray</b></tt> is an <i>overloaded</i> method within the Delphi
DLL. The original <tt><b>initializeByteArray</b></tt> method required an extra parameter: the number of times to
initialize the array. Obviously, you only need to initialize each member once, but the original version is used to
test the performance of initializing an array many times. However, this example only requires a single initialization, so I
overloaded the method and exported it with the name <tt><b>Java_Native_initializeByteArrayOnce</b></tt>. This has the benefits
of allowing both versions to be called from Java and also maintains compatibility with the existing Java code, namely
<A HREF="src/Main_java.html">Main.java</a>.
<pre class="sourcecode"><code><font color="#003399"><i>(****************************************************************************
* Java declaration:
* native public void initializeByteArrayOnce(byte[] byteArray, byte Value);
*
* Initializes each element of an array with the value 'Value'. This function
* is an overloaded version of the one used to test performance.
*
* Class: Native
* Method: initializeByteArrayOnce
* Signature: ([BB)V
*)</i></font>
<b>procedure</b> Java_Native_initializeByteArray(PEnv: PJNIEnv; Obj: JObject; ByteArray: JByteArray; Value: JByte); <b>stdcall</b>; <b>overload</b>;
<b>var</b>
Elements: PJByte;
PE: PJByte;
Len: DWORD;
I: Longint;
IsCopy: JBoolean;
JVM: TJNIEnv;
<b>begin</b>
JVM := TJNIEnv.Create(PEnv);
<font color="#003399"><i>// Get elements of the array.</i></font>
elements := JVM.GetByteArrayElements(ByteArray, IsCopy);
<font color="#003399"><i>// Get length of the array.</i></font>
Len := JVM.GetArrayLength(ByteArray);
<font color="#003399"><i>// Assign 'Value' to each element.</i></font>
PE := Elements;
<b>for</b> I := 0 <b>to</b> Len - 1 <b>do</b> <b>begin</b>
PE^ := Value;
Inc(PE);
<b>end</b>;
<font color="#003399"><i>// Release the elements of the array.</i></font>
JVM.ReleaseByteArrayElements(ByteArray, Elements, 0);
JVM.Free;
<b>end</b>;
</code></pre><hr width=30%>
<b>Notes:</b>
<ul>
<li>Because <i>elements</i> is a pointer and not an array type, you can't use array indexing (e.g. <tt>elements[i]</tt>). I use another
pointer to <i>walk</i> the array, thus preserving the value of <i>elements</i>, which is later passed to
<tt><b>ReleaseByteArrayElements</b></tt> to be released.
<li>Check out the description of the JNI methods
<a href="http://java.sun.com/products/jdk/1.2/docs/guide/jni/spec/functions.doc.html#17382">GetXXXArrayElements</a> and
<a href="http://java.sun.com/products/jdk/1.2/docs/guide/jni/spec/functions.doc.html#17440">ReleaseXXXArrayElements</a> on Sun's
site for details. You'll see an explanation of the parameters that are passed to these methods.
(These links explain it better than I can.)
</ul>
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>
<div class="SectionHeader">
<A NAME="Concept4">Concept #4 - Passing an Array of Objects to Delphi</A>
</div>
<BLOCKQUOTE>
This example shows how to access each element of an array of Java objects. For each object in the array, the obect's
<tt><b>toString</b></tt> method will be invoked on the object. Almost all Java objects implement the <tt><b>toString</b></tt>
method, which prints something intelligent about the value of the object. There are some interesting concepts to recognize here:
<ol>
<li>It doesn't matter what <i>kind</i> of object is in the array. This function will handle all types and the
<tt><b>toString</b></tt> method will act correctly (since it's being invoked on the object itself.)
<li>The DLL is actually calling back into Java to do the printing. This is because, although the object has been passed into
the DLL (as an element of the array), it's really only a reference to the object. The object's methods still exist in the Java
code that called the DLL.
</ol>
<pre class="sourcecode"><code><font color="#003399"><i>(****************************************************************************
* Java declaration:
* native public void printObjectArray(Object[] array);
*
* Given an array of Objects, each element is printed using the
* 'toString' method of the Object.
*
* Class: Native
* Method: printObjectArray
* Signature: ([Ljava/lang/Object;Z)V
*)</i></font>
<b>procedure</b> Java_Native_printObjectArray(PEnv: PJNIEnv; Obj: JObject; ObjArray: JObjectArray; Print: JBoolean); <b>stdcall</b>;
<b>var</b>
Cls: JClass;
Mid: JMethodID;
Ob: JObject;
Element: JObject;
S: JString;
Str: <b>string</b>;
Len, I: Integer;
JVM: TJNIEnv;
<b>begin</b>
JVM := TJNIEnv.Create(PEnv);
<font color="#003399"><i>// Get length of the array</i></font>
len := JVM.GetArrayLength(ObjArray);
<font color="#003399"><i>// Make sure we have at least one object in the array</i></font>
<b>if</b> Len < 1 <b>then</b>
exit;
<font color="#003399"><i>// Get an element of the array so we can get the right 'toString' method</i></font>
Ob := JVM.GetObjectArrayElement(ObjArray, 0);
<font color="#003399"><i>// Get the class associated with this object</i></font>
Cls := JVM.GetObjectClass(Ob);
<b>if</b> Cls = <b>nil</b> <b>then</b> <b>begin</b>
WriteLn(<font color="#9933CC">'Can'</font><font color="#9933CC">'t GetObjectClass'</font>);
exit;
<b>end</b>;
<font color="#003399"><i>// Get method ID for the 'toString' method of this object's class</i></font>
Mid := JVM.GetMethodID(Cls, <font color="#9933CC">'toString'</font>, <font color="#9933CC">'()Ljava/lang/String;'</font>);
<font color="#003399"><i>// We will check this also</i></font>
<b>if</b> Mid = <b>nil</b> <b>then</b> <b>begin</b>
WriteLn(<font color="#9933CC">'Can'</font><font color="#9933CC">'t GetMethodID for toString'</font>);
exit;
<b>end</b>;
<font color="#003399"><i>// Loop the the array of objects and print out each one using</i></font>
<font color="#003399"><i>// the 'toString' method of its ancestor class Object</i></font>
<b>for</b> I := 0 <b>to</b> Len - 1 <b>do</b> <b>begin</b>
<font color="#003399"><i>// Get the next element from the array</i></font>
Element := JVM.GetObjectArrayElement(ObjArray, i);
<font color="#003399"><i>// Call the 'toString' method, which returns a String representation</i></font>
<font color="#003399"><i>// of Rectangle object.</i></font>
S := JVM.CallObjectMethodA(Element, Mid, <b>nil</b>);
<font color="#003399"><i>// The actual printing can be turned on/off. This was useful during</i></font>
<font color="#003399"><i>// debugging when passing thousands of elements into the procedure.</i></font>
<b>if</b> Print <> False <b>then</b> <b>begin</b>
Str := JVM.JStringToString(S);
WriteLn(Str);
<b>end</b>;
<b>end</b>;
JVM.Free;
<b>end</b>;
</code></pre>
<hr width=30%>
<b>Notes:</b>
<ul>
<li>The <tt><b>GetMethodID</b></tt> function is used to locate the object's method that we want to invoke. This function
takes 4 arguments:
<ol>
<li><tt><b>env</b></tt> - The Java environment. This is the first parameter to all JNI functions.
<li><tt><b>cls</b></tt> - This is the object's class. We got this by calling the <tt><b>GetObjectClass</b></tt> function in the previous
line. The method that we want to invoke resides in the object's class somewhere.
<li><tt><b>'toString'</b></tt> - This is the name of the method. The name is case-sensitive.
<li><tt><b>'()Ljava/lang/String;'</b></tt> - This is the method's signature. The <tt><b>toString</b></tt> takes 0 parameters and returns
a String.
</ol>
<p>
<i>Additional note:</i> The <tt><b>GetMethodID</b></tt> only takes 3 arguments in the code above. This is because
we are using the TJNIEnv class to wrap the raw interface. Thus, the <tt><b>env</b></tt> parameter does not need to be passed
because it is part of the class. When the class in turn calls the JNI function <tt><b>GetMethodID</b></tt>, it will at that
time pass <tt><b>env</b></tt> as a parameter.
<p>
<li>The <tt><b>CallObjectMethodA</b></tt> function is what actually invokes (calls) the method. This function also
takes 4 arguments:
<ol>
<li><tt><b>env</b></tt> - The Java environment.
<li><tt><b>element</b></tt> - This is the object. In this example, <tt><b>element</b></tt> is one element of the array of objects.
<li><tt><b>mid</b></tt> - This is the ID of the method we are invoking. This is why we needed the <tt><b>GetMethodID</b></tt> above.
<li><tt><b>nil</b></tt> - The last parameter is an array of parameters to send to the method. Since the <tt><b>toString</b></tt>
method doesn't take any parameters, we pass <tt><b>nil</b></tt> to this function.
</ol>
<p>
<i>Additional note:</i> The <tt><b>CallObjectMethodA</b></tt> only takes 3 arguments in the code above. This is because
we are using the TJNIEnv class to wrap the raw interface. Thus, the <tt><b>env</b></tt> parameter does not need to be passed
because it is part of the class. When the class in turn calls the JNI function <tt><b>GetMethodID</b></tt>, it will at that
time pass <tt><b>env</b></tt> as a parameter.
<p>
<li>There is an assumption in this Delphi function in that it expects that each object in the array is the same kind of object. If
you look closely, you'll notice that the <tt><b>toString</b></tt> method is only <i>retrieved</i> from one object in the array.
This <i>methodID</i> is used within the loop to print the object. If we wanted to mix different kinds of objects in the array,
we would have to retrieve the <i>methodID</i> of the <tt><b>toString</b></tt> method from each object within the loop, which
would add a lot of overhead. (This Delphi procedure assumes that each element in the array is of the same kind.)
<p>
<li>The boolean parameter <i>Print</i> is a flag that tells the function whether or not to actually print anything. This
flag can be useful when passing very large arrays to the function (for testing purposes.) Also, there is a support function
called <tt><b>JStringToString</b></tt>, which is convenient for converting a Java String to a Delphi string. This function
is part of the TJNIEnv class.
</ul>
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>
<div class="SectionHeader">
<A NAME="Concept5">Concept #5 - Passing a 2-Dimensional Array to Delphi</A>
</div>
<BLOCKQUOTE>
This examples shows how to pass a 2-D array to Delphi. It is straight-forward to apply this technique to
3-D arrays as well.
<pre class="sourcecode"><code><font color="#003399"><i>(****************************************************************************
* Java declaration:
* native public void pass2DByteArray(byte[][] array);
*
* Pass a 2-D array of Bytes from Java. This method will retrieve each
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -