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

📄 delphi-jni-3.html

📁 JNI(java本地接口)之delphi版
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<a href="#Top">Back to top</a>
</blockquote>





<div class="SectionHeader">
<A NAME="DynamicLinking">Choosing the JRE and Version at Runtime</A>
</div>
<BLOCKQUOTE>
In the previous section, you saw how it was possible to use different versions of the JRE. Although each example used a different version of the
JRE, the version you chose to use still had to be known at compile time. In otherwords, both of the previous examples required that the user
have <tt>jvm.dll</tt> and/or <tt>javai.dll</tt> correctly installed on their computers. But what if you didn't want to "hard code" the version
(or name of the DLL) into your code? In this case, you'd need to use another method called <b><i>dynamic loading</i></b> or <b><i>explicit loading</i></b>. 
(The term <i>linking</i> is also used in place of the term <i>loading</i>.)
<p>
Technically, there are differences between dynamic/explicit loading and static/implicit loading. The default behavior of JNI.pas is to implicitly
load the JRE. There are pros and cons to this method, and some of those are:
<ul>
<li>The name of the DLL is hard-coded into your program. (Con)
<li>The DLL must be present and loaded into memory before your program runs. This can be undesirable if there are circumstances when your program
won't access any of the code in the DLL. (Con)
<li>Programming is simpler because the programmer doesn't have to load/unload and DLLs are locate procedure names. (Pro)
</ul>
Explicit loading also has its pros and cons:
<ul>
<li>The name of the is not hard-coded into the programmer and can change at runtime. (Pro)
<li>The DLL does not have to be loaded until it is actually going to be used. If the program never needs any code in the DLL, no time is
wasted loading it. (Pro)
<li>Programming is more complicated because the programmer is now responsible for loading/unload the DLL and locating procedure names. (Con)
</ul>
As you can see, items that are pros in one method are cons in another, and vice-versa. There certainly are other considerations, but these are
just a few of the common ones. By looking at these factors, one might conclude that explicit loading is a better way to go, since it has more
pros than cons. However, if many DLL procedures will always be required in a DLL, explicitly locating them could outweigh the pros. However, in
the case of JNI.pas, the wrapper class <tt>TJavaVM</tt> encapsulates the explicit loading/unloading so you, as the programmer, don't have to
worry about it. This may give explicit loading another positive vote.
<p>
Using the <i>Hello World!</i> example from above, we will see how to modify it to use dynamic loading, instead of static (implicit) loading. The change
is trivial. In fact, only two changes are required: one line of code is modified and a symbol is defined in the Project Options dialog. For brevity, I'm 
only going to show the line of code that differs between dynamic and static loading. Recall this line of code from the orignial program,
<tt><b>HelloWorld1.dpr</b></tt>:
<blockquote>
<pre class="sourcecode"><code><font color="#003399"><i>  // Create the JVM (using a wrapper class)</i></font>
JavaVM := TJavaVM.Create;
</pre></code>
</blockquote>
To modify this to work with the dynamic loading mechanisms, the line should be changed to this:
<blockquote>
<pre class="sourcecode"><code><font color="#003399"><i>  // Create the JVM (using a wrapper class)</i></font>
JavaVM := TJavaVM.Create(JNI_VERSION_1_2, <font color="#9933CC">'jvm.dll'</font>);
</pre></code>
</blockquote>
If you think about it, this makes sense. Since we are not using static loading (implicit linking), we must specify the necessary information.
This information is the version of the JRE we are loading (<tt>JNI_VERSION_1_2</tt>) and the name of the DLL that implements the JRE
(<tt>jvm.dll</tt>). Also, we must define the symbol <tt>DYNAMIC_LINKING</tt> in the Project Options dialog:
<p>
<blockquote>
<IMG SRC="gifs/Options2.gif" WIDTH=424 HEIGHT=399>
</blockquote>
<p>
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 the dynamic mechanisms won't be enabled. That's all there is to it. 
Because JNI.pas does all of the work of loading/unloading the DLL and locating the necessary procedures 
(using the Win API function <tt>GetProcAddress</tt>), the amount of extra work required to use this method is trivial.
<p>
<b>Notes</b>:
<BLOCKQUOTE>
It is possible to use a JRE that isn't in your PATH. To do this, you must specify the path to the JRE as in this code:
<blockquote>
<pre class="sourcecode"><code><font color="#003399"><i>  // Create the JVM (using a wrapper class)</i></font>
JavaVM := TJavaVM.Create(JNI_VERSION_1_2, <font color="#9933CC">'C:\jdk1.2.2\jre\bin\classic\jvm.dll'</font>);
</pre></code>
</blockquote>
or, to use a JDK 1.1 version of the JRE:
<blockquote>
<pre class="sourcecode"><code><font color="#003399"><i>  // Create the JVM (using a wrapper class)</i></font>
JavaVM := TJavaVM.Create(JNI_VERSION_1_1, <font color="#9933CC">'C:\jre1.1.6\bin\javai.dll'</font>);
</pre></code>
</blockquote>

Note that this DLL must be in the proper directory relative to other JRE support files. Refer to <a href="#Configuration">Configuring Your Machine</a>
above for details on installing a JRE.

</BLOCKQUOTE>
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>




<div class="SectionHeader">
<A NAME="Example2">A GUI Example</A>
</div>
<BLOCKQUOTE>
All of the previous examples have focused on using Delphi console applications to demonstrate accessing the Java Native Interface. This was primarily done to
keep the code as simple as possible. (Although it is arguable that the code necessary to implement simple GUI applications in Delphi is equally simple.) In the
example that follows, a more realistic Delphi application will be developed that will show you how to integrate Java code into your Delphi program. This example
is by far the most complex in this tutorial. However, if you've followed the tutorial up to this point, it won't seem like that big of a step. All of the previous
examples have worked towards bringing you to this point. You will actually only learn one new concept in this example: How to use the 
Java Native Interface with Delphi threads. We will see this technique later in the example.
<p>
For this example, we will develop a Delphi application that is a complete, stand-alone, GUI application. The application is called <tt><b>URLGrabber</b></tt> 
and it's purpose is to download HTML pages from the Internet to your computer, not unlike how a web browser would do it.
Through the UI, the user of the program must specify the URL of the web page to download (e.g. http://www.delphi-jedi.org/index.html)
<p>
The twist this time (there is always a twist &lt;g&gt;) is that the actually retrieving of the HTML document will be done by Java code. The name of the
Java object is <tt><b>PageGrabber</b></tt> and it is implemented in a file named <tt>PageGrabber.java</tt>. The Delphi application will create this Java
object and call a method, <tt>Fetch</tt>, which requires a single parameter of type <tt><b>string</b></tt>. This parameter is (you guessed it) the URL
of the HTML document to download. After the Java object completes the task of retrieving the HTML 
document, it returns this document to the Delphi application where it will be displayed in a TMemo component on the form.
<p>
<a href="zips/URLGrabber.zip">Download</a> the files for this project.</a>
<p>
<h4><A NAME="Example2JavaCode">The Java Code</A></h4>
From Delphi's point-of-view, there are only two methods of concern in this class. They are:
<blockquote>
<pre class="sourcecode"><code>  <b>public</b> <b>static</b> String FetchS(String inputURL)
  {
    PageGrabber pg = <b>new</b> PageGrabber();
    <b>return</b> pg.Fetch(inputURL);
  }

  <b>public</b> String Fetch(String inputURL)
  {
    <b>return</b> GetHTML(inputURL);
  }

</code></pre>
</blockquote>
The fundamental difference between them is that the first one, <tt>FetchS</tt> is a static (class) method, which simply calls the non-static method, <tt>Fetch</tt>.
This was done so that you can see the different coding that is required in Delphi to access these methods. You can consider these two methods as the public interface
of the <tt>PageGrabber</tt> class. However, the actual work is done by the private <tt>GetHTML</tt> method: (I've omitted everything except the non-essential parts)

<blockquote>
<pre class="sourcecode"><code>  <b>private</b> String GetHTML(String inputURL)
  {
    String html = <font color="#9933CC">&quot;&quot;</font>;
    String thisLine = <font color="#9933CC">&quot;&quot;</font>;
    URL url;

    <b>try</b>
    {
      url = <b>new</b> URL(inputURL);

      <b>try</b>
      {
        BufferedReader theData = <b>new</b> BufferedReader(<b>new</b> InputStreamReader(url.openStream()));
        <b>try</b>
        {
          <b>while</b> ((thisLine = theData.readLine()) != <b>null</b>)
          {
            html = html + thisLine;
          }
          <font color="#003399"><i>// *** Omitted exception handlers ***</i></font>
        }
      }
    }
  }
</code></pre>
</blockquote>
The full listing is in <a href="src/PageGrabber_java.html">PageGrabber.java</a>. There are a couple of points to be made here:
<ul>
<li>Both <tt>Fetch</tt> and <tt>FetchS</tt> return a string, which is the HTML content retrieved from the URL specified.
<li>There is a <tt><b>public static void</b> main</tt> method in the class so that this code can be tested from the commad line. To test it, simply run
it from the command line as in this example:
<blockquote>
<pre>
java PageGrabber http://www.yahoo.com
</pre>
</blockquote>
This will put the contents of the URL (www.yahoo.com) in a file called <tt>output.html</tt> in the same directory as <tt>PageGrabber.class</tt>.
</ul>

Since the point of this exercise is to demonstrate how to access this Java code
from Delphi, I'm not going to discuss the Java code. Also, there are probably a dozen other ways to accomplish this same task with Java. I have chosen to
implement in the way shown above. (Actually, I wrote this several years ago and thought that it would make a good sample for this tutorial. I don't claim 
that it is a good example of Java coding.)


<h4><A NAME="Example2DelphiCode">The Delphi Application</A></h4>
The application's main form is pictured below. This snapshot shows the HTML code for the home 
page of www.planetquake.com in a TMemo component. The HTML code was downloaded to a file named <tt>output.txt</tt> and the status bar shows that the
size of the file is 66016 bytes.
<p>
<blockquote>
<IMG SRC="gifs/URLGrabber.gif" WIDTH=600 HEIGHT=400>
</blockquote>
<p>
The main form contains eight primary UI elements:
<ul>
<li>A TMemo component - This is used to display the content of the HTML pages that were download, and to display the status of the download threads.
<li>A TComboBox - This contains a list of pre-defined URLs to download.
<li>A TStatusBar - This provides some visual feedback during the process.
<li>Five TButtons - Each button contains code to demonstrate a different function of the application:
<ol>
<li>Load VM - Create's the necessary classes and loads the Java VM
<li>Call class method - Demonstrates calling a class (static) Java method
<li>Call object method - Demonstrates calling an object (non-static) Java method
<li>Call method from thread - Demonstrates calling a Java method (object) from a thread other than the application's main thread
<li>Create multiple threads - Demonstrates creating multiple threads to retrieve all of the URLs simultaneously
</ol>
</ul>
The full source code for the main form is here: <a href="src/URLGrabberMainForm_pas.html">URLGrabberMainForm.pas</a>.
The project source is here: <a href="src/URLGrabber_dpr.html">URLGrabber.dpr</a>.
All of the files for this project are zipped here: <a href="zips/URLGrabber.zip">URLGrabber.zip</a>. 

<p>
<h4><A NAME="Example2Part1">Loading the Java VM</A></h4>
Creating the necessary Delphi objects and loading the VM for the application is pretty much the same as it was for the <i>Hello World!</i> example. The main
difference is that we store these objects in fields of the <tt>TForm1</tt> class. These fields are declared as:
<blockquote>
<pre class="sourcecode"><code>FJavaVM: TJavaVM;
FJNIEnv: TJNIEnv;
</pre></code>
</blockquote>
The code is to create the objects and load the VM is in the <tt>OnClick</tt> handler for the <tt>TButton btnLoadVM</tt> and is
pretty much self-explanatory:

<blockquote>
<pre class="sourcecode"><code><b>procedure</b> TURLGrabberForm.btnLoadVMClick(Sender: TObject);
<b>var</b>
  Errcode: Integer;
  VM_args11: JDK1_1InitArgs;
  VM_args: JavaVMInitArgs;
  Classpath: <b>string</b>;
  Options: <b>array</b> [0..10] <b>of</b> JavaVMOption;
<b>begin</b>
  UpdateStatusBar(<font color="#9933CC">'Loading VM...'</font>, True);

  <b>try</b>
      <font color="#003399"><i>// Create the wrapper for the VM</i></font>
    FJavaVM := TJavaVM.Create;

      <font color="#003399"><i>// Get default settings (so we can display them)</i></font>
    Errcode := JNI_GetDefaultJavaVMInitArgs(@VM_args11);
    <b>if</b> Errcode &lt; 0 <b>then</b>
    <b>begin</b>
      ShowMessageFmt(<font color="#9933CC">'JNI_GetDefaultJavaVMInitArgs failed, error code = %d'</font>, [Errcode]);
      Exit;
    <b>end</b>;

      <font color="#003399"><i>// Display the classpath (this is just for reference)</i></font>
    Classpath := VM_args11.classpath;
    mmoHTML.Lines.Add(<font color="#9933CC">'CLASSPATH='</font> + Classpath);

      <font color="#003399"><i>// Set up the options for the VM</i></font>

⌨️ 快捷键说明

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