⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 delphi-jni-3.html

📁 JNI(java本地接口)之delphi版
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<pre class="sourcecode"><code><font color="#003399"><i>    // Find the static method 'printHello' within the HWJava class</i></font>
  Mid := JNIEnv.GetStaticMethodID(Cls, <font color="#9933CC">'printHello'</font>, <font color="#9933CC">'()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 find method: printHello'</font>);
    Exit;
  <b>end</b>;
</code></pre>
If the Java method <tt>printHello</tt> is found, the variable <tt>Mid</tt> will contain a valid method ID, otherwise it will be <b>nil</b>. 
<tt>Mid</tt> is declared as:
<pre>
  Mid: JMethodID;
</pre>
Note that since the Java method <tt>HWJava.printHello</tt> is a <b>static</b> method, we use the <tt>GetStaticMethodID</tt> function, rather than the
<tt>GetMethodID</tt> function. Refer to <a href="delphi-jni-2.html#Concept4">Concept #4</a> from Part Two of the tutorial for an example
of the <tt>GetMethodID</tt> function. In the example, the <tt>GetStaticMethodID</tt> takes 3 parameters:
<ol>
<li><tt>Cls</tt> - The JClass that was retrieved previously by the <tt>TJNIEnv.FindClass</tt> method.
<li><tt>'printHello'</tt> - The name of the static method in the <tt>HWJava</tt> class. Remember: Java is case-sensitive.
<li><tt>'()V'</tt> - The signature of the <tt>printHello</tt> method. Refer to <a href="delphi-jni-2.html#Signatures">signatures</a> in Part Two for 
a refresher.
</ol>
</BLOCKQUOTE>


<b><a name="CloserLook7">Call the Java method</a></b>:
<BLOCKQUOTE>
<pre class="sourcecode"><code><font color="#003399"><i>    // Call the static method</i></font>
  JNIEnv.CallStaticVoidMethod(Cls, Mid, []);
</code></pre>
This is the actual call to the Java method. There are several points of interest with this code:
<ul>
<li>The <tt>TJNIEnv.CallStaticVoidMethod</tt> tells us a few things itself. 
<ul>
<li>The method we wish to call in the Java class is a <b>static</b> method or <b>class</b> method, as opposed to a method of a Java object.
This is evident from the word <tt><i>Static</i></tt> in <tt>Call<i>Static</i>VoidMethod</tt>.
<li>The method will not return a value. This is evident from the word <tt><i>Void</i></tt> in <tt>CallStatic<i>Void</i>Method</tt>.
</ul>
<p>
<li>There are 3 parameters required when calling any of the <tt>CallStatic<i>XXX</i>Method</tt> API functions. The types and meanings are:
<ol>
<li>A JClass - This is the value we obtained from the <tt>TJNIEnv.FindClass</tt> API call. This identifies the class that contains
the method we want to invoke. (Had we wanted to invoke a method of a Java object instead of a class, we would pass a value that identified
the object instead of the class. This object identifier is much like the <tt><b>Self</b></tt> object in Delphi or the <tt><b>this</b></tt>
pointer in Java.)
<li>A JMethodID - This is the method ID we obtained when we called <tt>TJNIEnv.GetStaticMethodID</tt>. 
<li>An array of parameters - These are the parameters that will be passed to the Java method we are calling. In our example, there
are no parameters, so we pass an empty array.
</ul>
</ul>
In a later example, we will see how to call methods in a Java object as well as pass parameters to the methods and return values from them.
</BLOCKQUOTE>
Well, that is pretty much how you call a Java method from a Delphi program. There are many variations on this, depending on the
circumstances of the Java method. Also note that you would want to call <tt>Free</tt> the <tt>TJavaVM</tt> and <tt>TJNIEnv</tt> 
objects within this code, but that was left out to keep the code simple. I encourage you to try this on your own and prove to 
yourself that this works. Once you've got this simple example working, you can move on to more ambitious code.
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>





<div class="SectionHeader">
<A NAME="DifferentVersion">Using a Different JRE Version</A>
</div>
<BLOCKQUOTE>
By default, JNI.pas implicitly loads <tt>jvm.dll</tt>, the implementation of the JRE in the Java 2 platform. However, it is possible to use a
different version of the JRE. Before Java 2, the JRE was implemented in <tt>javai.dll</tt>, which was part of the JDK 1.1.x distribution. For the most
part, you can interchange the two JREs. Some features were added to the Java 2 JRE, but we won't explore any of those in this tutorial.
<p>
The new version is called <tt><b>HelloWord2.dpr</b></tt> and is shown below. I put comments next to the 2 new variable declarations, and
also around a block of code which is different from the previous version. These differences are discussed after this code:
<p>
<pre class="sourcecode"><code><b>program</b> HelloWorld2;

<font color="#003399"><i>{$APPTYPE CONSOLE}</i></font>

<b>uses</b>
  SysUtils, JNI;

<b>var</b>
  VM_args11: JDK1_1InitArgs; <font color="#003399"><i>// this is different</i></font>
  Classpath: <b>string</b>;         <font color="#003399"><i>// this is different</i></font>
  JavaVM: TJavaVM;           
  JNIEnv: TJNIEnv;
  Cls: JClass;
  Mid: JMethodID;
  Errcode: Integer;
<b>begin</b>

  <b>try</b>

      <font color="#003399"><i>// Create the JVM (using a wrapper class)</i></font>
    JavaVM := TJavaVM.Create;

    <font color="#003399"><i>// ********* this is different, start *****************</i></font>

        <font color="#003399"><i>// Set the options for the VM</i></font>
      Errcode := JNI_GetDefaultJavaVMInitArgs(@vm_args11);
      <b>if</b> Errcode &lt; 0 <b>then</b>
      <b>begin</b>
        WriteLn(Format(<font color="#9933CC">'JNI_GetDefaultJavaVMInitArgs failed, error code = %d'</font>, [Errcode]));
        Exit;
      <b>end</b>;

        <font color="#003399"><i>// Add the current directory to the classpath</i></font>
      Classpath := vm_args11.classpath + <font color="#9933CC">';.'</font>;
      vm_args11.classpath := PChar(Classpath);

        <font color="#003399"><i>// Load the VM</i></font>
      Errcode := JavaVM.LoadVM(vm_args11);

    <font color="#003399"><i>// ********* this is different, end *****************</i></font>

    <b>if</b> Errcode &lt; 0 <b>then</b>
    <b>begin</b>
      WriteLn(Format(<font color="#9933CC">'Error loading JavaVM, error code = %d'</font>, [Errcode]));
      Exit;
    <b>end</b>;

      <font color="#003399"><i>// Create a Java environment from the JVM's Env (another wrapper class)</i></font>
    JNIEnv := TJNIEnv.Create(JavaVM.Env);

      <font color="#003399"><i>// Find the class in the file system. This is why we added</i></font>
      <font color="#003399"><i>// the current directory to the Java classpath above.</i></font>
    Cls := JNIEnv.FindClass(<font color="#9933CC">'HWJava'</font>);
    <b>if</b> Cls = <b>nil</b> <b>then</b>
    <b>begin</b>
      WriteLn(<font color="#9933CC">'Can'</font><font color="#9933CC">'t find class: HWJava'</font>);
      Exit;
    <b>end</b>;

      <font color="#003399"><i>// Find the static method 'printHello' within the HWJava class</i></font>
    Mid := JNIEnv.GetStaticMethodID(Cls, <font color="#9933CC">'printHello'</font>, <font color="#9933CC">'()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 find method: printHello'</font>);
      Exit;
    <b>end</b>;

      <font color="#003399"><i>// Call the static method</i></font>
    JNIEnv.CallStaticVoidMethod(Cls, Mid, []);

  <b>except</b>
    <b>on</b> E : Exception <b>do</b>
      WriteLn(<font color="#9933CC">'Error: '</font> + E.Message);
  <b>end</b>;
<b>end</b>.
</code></pre>

The first thing to notice is that there are two new variable declarations:
<BLOCKQUOTE>
<pre class="sourcecode"><code>VM_args11: JDK1_1InitArgs; <font color="#003399"><i>// this is different</i></font>
Classpath: <b>string</b>;         <font color="#003399"><i>// this is different</i></font>
</code></pre>
</BLOCKQUOTE>

The original code used <tt>JavaVMInitArgs</tt> and <tt>JavaVMOptions</tt>. Early versions of the JRE require a variable of type 
<tt>JDK1_1InitArgs</tt>. The <tt>Classpath</tt> variable (a <b><tt>string</tt></b>) is simply a variable to hold a newly constructed
classpath, which is required by the JDK 1.1.x.
<p>
I've added numbers to the new block of code to explain some details.

<BLOCKQUOTE>
<pre class="sourcecode"><code><font color="#003399"><i>       // Set the options for the VM</i></font>
 1.  Errcode := JNI_GetDefaultJavaVMInitArgs(@VM_args11);
 2.  <b>if</b> Errcode &lt; 0 <b>then</b>
 3.  <b>begin</b>
 4.   WriteLn(Format(<font color="#9933CC">'JNI_GetDefaultJavaVMInitArgs failed, error code = %d'</font>, [Errcode]));
 5.   Exit;
 6.  <b>end</b>;

<font color="#003399"><i>       // Add the current directory to the classpath</i></font>
 7.  Classpath := VM_args11.classpath + <font color="#9933CC">';.'</font>;
 8.  VM_args11.classpath := PChar(Classpath);

<font color="#003399"><i>       // Load the VM</i></font>
10.  Errcode := JavaVM.LoadVM(VM_args11);
</code></pre>
</BLOCKQUOTE>

<b>Details about the above code:</b>
<ul>
<li>Line 1 - This API function retrieves the current default settings from the JRE. The only setting we are interested in at this time is
the <tt>classpath</tt> value. This is because we need to add the current directory to it. With the JRE in the Java 2 platform, we didn't 
have to modify the <tt>classpath</tt> itself. We simply needed to add the current directory as an additional directory to be searched
by the JRE.
<li>Line 7 - We simple extract the value of <tt>classpath</tt> from the <tt>VM_args11</tt> <tt><b>record</b></tt> and add <tt>;.</tt> to it, which 
is the notation for the current directory.
<li>Line 8 - We assign the new value of <tt>classpath</tt> back to the <tt>VM_args11</tt> <tt><b>record</b></tt>. (Line 7 and 8 could have been 
done in one step, but I separated them to make it clear.)
<li>Line 10 - We load the VM passing a parameter of type <tt>JDK1_1InitArgs</tt>. The previous example passed a parameter of type
<tt>JavaVMInitArgs</tt>. However, this is allowable because the <tt>TJavaVM.LoadVM</tt> method is <tt><b>overloaded</b></tt> to accept either type and the
correct JNI API will be called internally.
</ul>

There is one last thing that needs to be done. You must define the symbol <tt>JDK1_1</tt> in the Project Options dialog. 
<p>
<blockquote>
<IMG SRC="gifs/Options1.gif" WIDTH=424 HEIGHT=399>
</blockquote>
<p>
This will cause the  JDK 1.1-specific code in JNI.pas to become enabled. 
Because the compiler needs to know which version (hence, which DLL) to link into your program, this has to be done. 
If you look in JNI.pas, you will see this code:
<pre class="sourcecode"><code>    <font color="#003399"><i>{$IFDEF JDK1_1}</i></font>
    JvmModuleName = <font color="#9933CC">'javai.dll'</font>;
    <font color="#003399"><i>{$ELSE}</i></font>
    JvmModuleName = <font color="#9933CC">'jvm.dll'</font>;
    <font color="#003399"><i>{$ENDIF}</i></font>
</code></pre>
Note that you must <b>Build</b> the project after defining a symbol in this dialog. If you just do a <b>Compile</b>, JNI.pas will not be
recompiled and the portions that support version switching won't be enabled.
You may have noticed that, so far, you haven't had to specify the names of these DLLs anywhere in your code.
This is why (if you use the default static loading of the JRE) you must have <tt>javai.dll</tt> (for JDK 1.1) and/or <tt>jvm.dll</tt> (for Java 2) in
your PATH. Later, we will see how to use a different DLL that's not in your PATH.
<p>
<b>Notes:</b>
<p>
<blockquote>
At this time, JNI.pas only supports two versions of the JRE, JDK 1.1 (1.1) and Java 2 (1.2). Sun has release a version 1.3 of the Java 2 platform 
which I haven't included yet. I suspect that it will not be difficult to add this capability. The source to JNI.pas is available to you, so if you
need version 1.3 support now, you should have little problems adding it.
<p>
A good way to understand what's in the value of <tt>classpath</tt> in the <tt>VM_args11</tt> <tt><b>record</b></tt> is to print the value at runtime. For
example, on my machine, when I print out this value using both versions, this is what it shows:
<p>
<b>JDK 1.1.6</b> (The <tt>classpath</tt> is actually a single line. I broke it up so it would fit here.)
<pre>
  n:\jre1.1.6\bin\..\classes;n:\jre1.1.6\bin\..\lib\classes.zip;
  n:\jre1.1.6\bin\..\lib\classes.jar;
  n:\jre1.1.6\bin\..\lib\rt.jar;
  n:\jre1.1.6\bin\..\lib\i18n.jar
</pre>

<b>Java 2</b> (The <tt>classpath</tt> is actually a single line. I broke it up so it would fit here.)
<pre>
  N:\Borland\JBuilder35\jdk1.2.2\jre\lib\rt.jar;
  N:\Borland\JBuilder35\jdk1.2.2\jre\lib\i18n.jar;
  N:\Borland\JBuilder35\jdk1.2.2\jre\classes
</pre>

Note that this is <i>before</i> adding the current directory to the <tt>classpath</tt>. Also, the <tt>CLASSPATH</tt> environment variable 
has no affect on the JRE's <tt>classpath</tt>.
</BLOCKQUOTE>
<p>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -