📄 delphi-jni-2.html
字号:
* element from the arrays, print it, then multiply it by 10 and store
* the new Value back into the array. This is to show access/updating
* of 2-D arrays within native code. The process would be similar for
* 3-D arrays.
*
* Class: Native
* Method: pass2DByteArray
* Signature: ([[B)V
*)</i></font>
<b>procedure</b> Java_Native_pass2DByteArray(PEnv: PJNIEnv; Obj: JObject; Array2D: JObjectArray); <b>stdcall</b>;
<b>var</b>
NumArrays: UINT;
Len: UINT;
I, J: Integer;
JBArray: JByteArray;
Elements: PJByte;
IsCopy: JBoolean;
JVM: TJNIEnv;
<b>begin</b>
JVM := TJNIEnv.Create(PEnv);
<font color="#003399"><i>// Get length of the array (number of arrays)</i></font>
NumArrays := JVM.GetArrayLength(Array2D);
WriteLn(<font color="#9933CC">'In native method printing 2-D byte array.'</font>);
WriteLn(<font color="#9933CC">'Each element is then multiplied by 10 and updated.'</font>);
<font color="#003399"><i>// Loop over each array</i></font>
<b>for</b> I := 0 <b>to</b> NumArrays - 1 <b>do</b> <b>begin</b>
<font color="#003399"><i>// Get the object at the i'th position (it's an array of Bytes)</i></font>
JBArray := JVM.GetObjectArrayElement(Array2D, i);
<font color="#003399"><i>// Get the length of this array of Bytes</i></font>
Len := JVM.GetArrayLength(JBArray);
<font color="#003399"><i>// Get the elements from the Byte array</i></font>
Elements := JVM.GetByteArrayElements(JBArray, IsCopy);
<font color="#003399"><i>// Print each element, then multiply the Value by 10</i></font>
<font color="#003399"><i>// and put it back into the array. (This is to prove that</i></font>
<font color="#003399"><i>// the array elements have changed when this function returns</i></font>
<font color="#003399"><i>// to Java code.</i></font>
<b>for</b> J := 0 <b>to</b> Len - 1 <b>do</b> <b>begin</b>
WriteLn(Format(<font color="#9933CC">'%d,%d = %d'</font>, [I, J, Elements^]));
<font color="#003399"><i>// Update the element (just multiply it by 10)</i></font>
Elements^ := Elements^ * 10;
Inc(Elements);
<b>end</b>;
<b>end</b>;
JVM.Free;
<b>end</b>;
</code></pre>
<hr width=30%>
<b>Notes:</b>
<ul>
<li>The important idea here to understand is that a 2-dimensional array is simply an array of arrays.
Because of this, the code above uses a nested loop to access each element of each array. For a 3D array,
you would nest another loop.
</ul>
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>
<div class="SectionHeader">
<A NAME="Concept6">Concept #6 - Calling a Method of a Java Object from Delphi</A>
</div>
<BLOCKQUOTE>
(See Concept #4, <a href="#Concept4">Passing an Array of Objects to Delphi</a> for the example.)
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>
<div class="SectionHeader">
<A NAME="Concept7">Concept #7 - Creating Java Objects within Delphi</A>
</div>
<BLOCKQUOTE>
The example shows how to create a Java object within the Delphi DLL. An array of Java objects is created
and then passed back to Java. The objects created are rectangles (<tt><b>java/awt/Rectangle</b></tt>) and we don't
do anything special with them. Each Rectangle is simply created and initialized to an arbitrary size. Then, they
are passed back to Java where they are printed out (using the object's <tt><b>toString</b></tt> method) for verification.
<pre class="sourcecode"><code><font color="#003399"><i>(****************************************************************************
* Java declaration:
* native public jobjectarray returnRectArray(int size);
*
* This function creates an array of 'size' Rectangles and returns them
* to the Java caller. First, the array is created, then each element
* is assigned a Rectangle object.
*
* Class: Native
* Method: returnRectArray
* Signature: (I)[Ljava/awt/Rectangle;
*)</i></font>
<b>function</b> Java_Native_returnRectArray(PEnv: PJNIEnv; Obj: JObject; Size: JInt): JObjectArray; <b>stdcall</b>;
<b>var</b>
Cls: JClass;
Mid: JMethodID;
Element: JObject;
JOArray: JObjectArray;
I: Integer;
Args: <b>array</b>[0..3] <b>of</b> JValue;
JVM: TJNIEnv;
<b>begin</b>
JVM := TJNIEnv.Create(PEnv);
<font color="#003399"><i>// Find the Rectangle class</i></font>
Cls := JVM.FindClass(<font color="#9933CC">'java/awt/Rectangle'</font>);
<b>if</b> Cls = <b>nil</b> <b>then</b> <b>begin</b>
WriteLn(<font color="#9933CC">'Can'</font><font color="#9933CC">'t FindClass(java/awt/Rectangle'</font>);
Result := <b>nil</b>;
exit;
<b>end</b>;
<font color="#003399"><i>// Get its constructor (the one that takes 4 integers)</i></font>
Mid := JVM.GetMethodID(Cls, <font color="#9933CC">'<init>'</font>, <font color="#9933CC">'(IIII)V'</font>);
<b>if</b> Mid = <b>nil</b> <b>then</b> <b>begin</b>
WriteLn(<font color="#9933CC">'Can'</font><font color="#9933CC">'t get MethodID for Rectangle constructor'</font>);
Result := <b>nil</b>;
exit;
<b>end</b>;
<font color="#003399"><i>// Allocate the array of Rectangles</i></font>
JOArray := JVM.NewObjectArray(Size, Cls, <b>nil</b>);
<font color="#003399"><i>// Now initialize each one to a Rectangle</i></font>
<b>for</b> I := 0 <b>to</b> Size - 1 <b>do</b> <b>begin</b>
<font color="#003399"><i>// Create a new Rectangle object</i></font>
Args[0].i := 0;
Args[1].i := 0;
Args[2].i := 5 * I;
Args[3].i := 10 * I;
Element := JVM.NewObjectA(Cls, Mid, @args);
<font color="#003399"><i>// Assign the Rectangle to an element of the array</i></font>
JVM.SetObjectArrayElement(JOArray, I, Element);
<b>end</b>;
<font color="#003399"><i>// Return the array back to the Java client</i></font>
Result := JOArray;
JVM.Free;
<b>end</b>;
</code></pre>
<hr width=30%>
<b>Notes:</b>
<ul>
<li>Notice how Delphi calls the constructor for a Rectangle object:
<pre>
<font color="#003399"><i>// Get its constructor (the one that takes 4 integers)</i></font>
mid := env.GetMethodID(env, cls, <font color="#9933CC">'<init>'</font>, <font color="#9933CC">'(IIII)V'</font>);
</pre>
The name of the constructor is <tt><b><init></b></tt>. In Java, a constructor's name is the same as its class, but when
invoking a constructor from native code, you use the name <tt><b><init></b></tt>. You still must specify the signature,
though, because a class can have more than one constructor and we must indicate which constructor we want.
</ul>
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>
<div class="SectionHeader">
<A NAME="Concept8">Concept #8 - Accessing Fields of a Java Object from Delphi</A>
</div>
<BLOCKQUOTE>
This example shows how to access fields (members) of the Java object/class. There are four fields declared in the class, each
demonstrates a particular point:
<ol>
<li><tt><b>public</b> String w</tt> - a public object, demonstrates accessing an <i>object</i>.
<li><tt><b>public int</b> x</tt> - a public int, demonstrates accessing a <i>primitive</i> type.
<li><tt><b>private int</b> y</tt> - a private int, demonstrates accessing a <i>private</i> field. (Yes!)
<li><tt><b>public static int</b> z</tt> - a public static int, demonstrates accessing a <i>static</i> field. (class member)
</ol>
<pre class="sourcecode"><code><font color="#003399"><i>(****************************************************************************
* Java declaration:
* native public void printWXYZ();
*
* Prints out four members of the Native object, w, x, y, and z. This
* function acts sort of like the traditional 'toString' method in Java.
* The members are declared in Native.java as such:
*
* public String w; // public Object
* public int x; // public
* private int y; // private (no protection here)
* public static int z; // public static
*
* The return Value from each GetFieldID call should be checked.
* I don't check because I'm trying to keep the focus on the calls.
*
* Class: Native
* Method: printWXYZ
* Signature: ()V
*)</i></font>
<b>procedure</b> Java_Native_printWXYZ(PEnv: PJNIEnv; Obj: JObject); <b>stdcall</b>;
<b>var</b>
X, Y, Z: JInt;
W: JString;
FID: JFieldID;
Cls: JClass;
Str: <b>string</b>;
JVM: TJNIEnv;
<b>begin</b>
JVM := TJNIEnv.Create(PEnv);
Cls := JVM.GetObjectClass(Obj);
<font color="#003399"><i>// w is a String</i></font>
FID := JVM.GetFieldID(Cls, <font color="#9933CC">'w'</font>, <font color="#9933CC">'Ljava/lang/String;'</font>);
<font color="#003399"><i>// Get the Object (String) w.</i></font>
W := JVM.GetObjectField(Obj, FID);
<font color="#003399"><i>// x is a non-static public field</i></font>
FID := JVM.GetFieldID(Cls, <font color="#9933CC">'x'</font>, <font color="#9933CC">'I'</font>);
<font color="#003399"><i>// Get the int</i></font>
X := JVM.GetIntField(Obj, FID);
<font color="#003399"><i>// y is a non-static private field, same as public here</i></font>
FID := JVM.GetFieldID(Cls, <font color="#9933CC">'y'</font>, <font color="#9933CC">'I'</font>);
<font color="#003399"><i>// Get the int</i></font>
Y := JVM.GetIntField(Obj, FID);
<font color="#003399"><i>// z is a _static_ public field, so call different method</i></font>
FID := JVM.GetStaticFieldID(Cls, <font color="#9933CC">'z'</font>, <font color="#9933CC">'I'</font>);
<font color="#003399"><i>// Get static int</i></font>
Z := JVM.GetStaticIntField(Cls, FID);
<font color="#003399"><i>// Convert Java string into Delphi string</i></font>
Str := JVM.JStringToString(W);
<font color="#003399"><i>// Sort of like the traditional 'toString' output</i></font>
WriteLn(Format(<font color="#9933CC">'[w = %s, x = %d, y = %d, z = %d]'</font>, [Str, X, Y, Z]));
JVM.Free;
<b>end</b>;
</code></pre>
<hr width=30%>
<b>Notes:</b>
<ul>
<li>There is no protection for <tt><b>private</b></tt> fields when accessing them using the JNI.
The method for accessing a <tt><b>private</b></tt> field is the same as accessing a <tt><b>public</b></tt> field.
<li>Accessing a <tt><b>static</b></tt> field is slightly different. It requires using one of the
<tt>GetStaticXXX</tt> functions of the JNI, instead of the plain <tt>GetXXX</tt> functions. This is because a
<tt><b>static</b></tt> field is part of a class and not part of an object.
</ul>
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>
<div class="SectionHeader">
<A NAME="Concept9">Concept #9 - Handling Java Exceptions within Delphi</A>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -