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

📄 delphi-jni-3.html

📁 JNI(java本地接口)之delphi版
💻 HTML
📖 第 1 页 / 共 5 页
字号:
    FillChar(Options, SizeOf(Options), #0);
    Options[0].optionString := <font color="#9933CC">'-Djava.class.path=.'</font>;
    VM_args.version := JNI_VERSION_1_2;
    VM_args.options := @Options;
    VM_args.nOptions := 1;

      <font color="#003399"><i>// Load the VM</i></font>
    Errcode := FJavaVM.LoadVM(VM_args);
    <b>if</b> Errcode &lt; 0 <b>then</b>
    <b>begin</b>
        <font color="#003399"><i>// Loading the VM more than once will cause this error</i></font>
      <b>if</b> Errcode = JNI_EEXIST <b>then</b>
        MessageDlg(<font color="#9933CC">'Java VM has already been loaded. Only one VM can be loaded.'</font>, mtError, [mbOK], 0)
      <b>else</b>
        ShowMessageFmt(<font color="#9933CC">'Error creating JavaVM, code = %d'</font>, [Errcode]);
      Exit;
    <b>end</b>;

      <font color="#003399"><i>// Create the Env class</i></font>
    FJNIEnv := TJNIEnv.Create(FJavaVM.Env);

      <font color="#003399"><i>// Enable UI buttons</i></font>
    btnCallClassMethod.Enabled := True;
    btnCallObjectMethod.Enabled := True;
    btnCallMethodFromThread.Enabled := True;
    btnCreateMultipleThreads.Enabled := True;

  <b>except</b>
    <b>on</b> E: Exception <b>do</b>
    <b>begin</b>
      ShowMessage(<font color="#9933CC">'Error: '</font> + E.Message);
      UpdateStatusBar(<font color="#9933CC">'Load VM failed.'</font>);
      Exit;
    <b>end</b>;
  <b>end</b>;

  UpdateStatusBar(<font color="#9933CC">'Ready.'</font>);
<b>end</b>;
</code></pre>
</blockquote>

<p>
<h4><A NAME="Example2Part2">Calling a Class Method</A></h4>
Calling a class (static) method of a Java class is slightly simpler than calling an object (non-static) method. This is because no Java object is required.
In other words, you don't have to create an object from the class before calling the method. The Java class <tt>PageGrabber</tt> has a static method
called <tt>FetchS</tt>. (The 'S' at the end is to remind you that it's the static version of the <tt>Fetch</tt> method, which is also in the class.) The
code to locate the class, locate the static method, and call the method, is shown below:

<blockquote>
<pre class="sourcecode"><code><b>procedure</b> TURLGrabberForm.btnCallClassMethodClick(Sender: TObject);
<b>var</b>
  Cls: JClass;
  Mid: JMethodID;
  HTML: <b>string</b>;
  URL: <b>string</b>;
  JStr: JString;
<b>begin</b>
  UpdateStatusBar(<font color="#9933CC">'Calling class method...'</font>, True);

  <b>try</b>
      <font color="#003399"><i>// Get the URL from the UI</i></font>
    URL := lstURLs.Text;

      <font color="#003399"><i>// Find PageGrabber class</i></font>
    Cls := FJNIEnv.FindClass(<font color="#9933CC">'PageGrabber'</font>);
    <b>if</b> Cls = <b>nil</b> <b>then</b>
    <b>begin</b>
      ShowMessage(<font color="#9933CC">'Can'</font><font color="#9933CC">'t find class: PageGrabber'</font>);
      Exit;
    <b>end</b>;

      <font color="#003399"><i>// Locate static method 'FetchS' in class</i></font>
    Mid := FJNIEnv.GetStaticMethodID(Cls, <font color="#9933CC">'FetchS'</font>, <font color="#9933CC">'(Ljava/lang/String;)Ljava/lang/String;'</font>);
    <b>if</b> Mid = <b>nil</b> <b>then</b>
    <b>begin</b>
      ShowMessage(<font color="#9933CC">'Can'</font><font color="#9933CC">'t find method: FetchS'</font>);
      Exit;
    <b>end</b>;

      <font color="#003399"><i>// Call the static method</i></font>
    JStr := FJNIEnv.CallStaticObjectMethod(Cls, Mid, [URL]);

      <font color="#003399"><i>// Convert the returned JString to a Delphi string</i></font>
    HTML := FJNIEnv.JStringToString(JStr);

      <font color="#003399"><i>// Display the HTML</i></font>
    mmoHTML.Lines.Add(HTML);

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

  UpdateStatusBar(<font color="#9933CC">'Ready.'</font>);
<b>end</b>;
</code></pre>
</blockquote>


<h4><A NAME="Example2Part3">Calling an Object Method</A></h4>
Calling a Java method on an object is slightly different than calling a static method. A non-static method requires an object to be 
constructed before it can be called. The Java class <tt>PageGrabber</tt> contains a non-static method called <tt>Fetch</tt>. In order to
call this method, an instance of the <tt>PageGrabber</tt> class must first be constructed. Since this construction of an object (using a constructor
of the class) is common, I've put this code into a global function called <tt>CreateJavaObject</tt>. This is the code for the <tt>CreateJavaObject</tt>
utility function:

<blockquote>
<pre class="sourcecode"><code><b>function</b> CreateJavaObject(<b>const</b> JNIEnv: TJNIEnv; <b>const</b> ClassName: <b>string</b>; <b>var</b> AClass: JClass): JObject;
<b>var</b>
  Mid: JMethodID;
<b>begin</b>
  Result := <b>nil</b>;

    <font color="#003399"><i>// Find the class</i></font>
  <b>try</b>
    AClass := JNIEnv.FindClass(PChar(ClassName));
    <b>if</b> AClass = <b>nil</b> <b>then</b>
      Exit;

      <font color="#003399"><i>// Get its default constructor</i></font>
    Mid := JNIEnv.GetMethodID(AClass, <font color="#9933CC">'&lt;init&gt;'</font>, <font color="#9933CC">'()V'</font>);
    <b>if</b> Mid = <b>nil</b> <b>then</b>
      exit;

      <font color="#003399"><i>// Create the object</i></font>
    Result := JNIEnv.NewObjectA(AClass, Mid, <b>nil</b>);

  <b>except</b>
    <b>on</b> E: Exception <b>do</b>
      ShowMessage(<font color="#9933CC">'Error: '</font> + E.Message);
  <b>end</b>;
<b>end</b>;
</code></pre>
</blockquote>
There are two inputs and two outputs for this function. The inputs are the Java environment, <tt>JNIEnv: TJNIEnv</tt>, and the class name, 
<tt>Classname: <b>string</b></tt>. The function returns two pieces of information to the caller. 
The first value it returns (via the <tt>Result</tt> variable) is the 
newly created Java object. The second value is a <tt>JClass</tt> value, which is returned through the <tt>JClass <b>var</b></tt> parameter. This value
is required by the calling function.
<p>
Note that this isn't a general-purpose object creation function because the signature of the constructor (<tt>'()V'</tt>) is hard-coded into the function. This
function just gives you an idea of some of the utility functions that you may want to write to handle very common cases in your coding. This function is used
by the <tt>OnClick</tt> handler for the <tt>TButton btnCallObjectMethod</tt>. This code shows the Java class being located, the object being constructed 
(via the <tt>CreateJavaObject</tt> function), the <tt>Fetch</tt> method being located and called. 

<blockquote>
<pre class="sourcecode"><code><b>procedure</b> TURLGrabberForm.btnCallObjectMethodClick(Sender: TObject);
<b>var</b>
  Cls: JClass;
  Mid: JMethodID;
  PageGrabber: JObject;
  JStr: JString;
  URL, HTML: <b>string</b>;
<b>begin</b>
  UpdateStatusBar(<font color="#9933CC">'Calling object method...'</font>, True);

  <b>try</b>
      <font color="#003399"><i>// Get the URL from the UI</i></font>
    URL := lstURLs.Text;

      <font color="#003399"><i>// Construct PageGrabber object</i></font>
    PageGrabber := CreateJavaObject(FJNIEnv, <font color="#9933CC">'PageGrabber'</font>, Cls);
    <b>if</b> PageGrabber = <b>nil</b> <b>then</b>
    <b>begin</b>
      ShowMessage(<font color="#9933CC">'Can'</font><font color="#9933CC">'t create PageGrabber object'</font>);
      Exit;
    <b>end</b>;

      <font color="#003399"><i>// Locate the 'Fetch' method</i></font>
    Mid := FJNIEnv.GetMethodID(Cls, <font color="#9933CC">'Fetch'</font>, <font color="#9933CC">'(Ljava/lang/String;)Ljava/lang/String;'</font>);
    <b>if</b> Mid = <b>nil</b> <b>then</b>
    <b>begin</b>
      ShowMessage(<font color="#9933CC">'Can'</font><font color="#9933CC">'t find method: Fetch'</font>);
      Exit;
    <b>end</b>;

      <font color="#003399"><i>// Call the method</i></font>
    JStr := FJNIEnv.CallObjectMethod(PageGrabber, Mid, [URL]);

      <font color="#003399"><i>// Convert the returned JString to a Delphi string</i></font>
    HTML := FJNIEnv.JStringToString(JStr);

      <font color="#003399"><i>// Display the HTML</i></font>
    mmoHTML.Lines.Add(HTML);

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

  UpdateStatusBar(<font color="#9933CC">'Ready.'</font>);
<b>end</b>;
</code></pre>
</BLOCKQUOTE>


<h4><A NAME="Example2Part4">Calling a Method from Another Thread</A></h4>
Now, things get a little more interesting. As is the case with most threaded code, some special care needs to be taken to ensure proper functionality of 
the threads. The Java Native Interface is no exception. To solve the basic threading issues, only a few extra API calls need to be made. 
<p>
There are some guidelines to follow if native (Delphi) code is to access the JNI from multiple threads. I am not going to go into a lot of detail
about it because that is a larger topic that is beyond the scope of this tutorial. However, the place to go for detailed answers 
is Sheng Liang's <i>The Java Native Interface - Programmer's Guide and Specification</i>. I highly recommend this book to anyone interested in 
doing any serious JNI programming. A link to this book is in the <a href="delphi-jni-1.html#Resources">resources</a> section.
<p>
One of the points that is stressed when accessing the JNI from native threads is that you must not pass the JNIEnv pointer from one thread to another. The
JNIEnv pointer is only valid in the thread that created it (or obtained it), so you can't save it or cache it for use in more than one thread. 
If you remember from the <tt>btnLoadVM</tt> event handler for the <tt>OnClick</tt> event, you will recall that we stored this pointer in a field of
the <tt>TFrmMain</tt> class called <tt>FJNIEnv</tt>. 
<p>
Now, you may be asking yourself, <i>"If you can't store this value, why did we do that?"</i>. The reason
is because we didn't use it in more than one thread. The UI for the application itself is running in a single thread, the main thread, and so any code that
is executing in the main thread can use this stored value. The problem arises when you create other threads (worker threads) that need to access this
JNIEnv pointer. And that is exactly what we are going to do now.
<p>

<b>Important Linux Note:</b>
<div class=Note1>
In order to have Kylix interact with Java from threads other than the main UI thread, you must use version 1.3 of the Java Runtime Environment. 
You can download the Java 2 Runtime Environment version 1.3 for Linux from Sun's website 
<a href="http://java.sun.com/j2se/1.3/jre/download-linux.html">here</a>. 
</div>
<p>

The first thing we need to do is to derive a new class that is based on the <tt>TThread</tt> class. This class is shown here:
<blockquote>
<pre class="sourcecode"><code>  TURLThread = <b>class</b>(TThread)
  <b>public</b>
    URL: <b>string</b>;
    HTML: <b>string</b>;
    JavaVM: TJavaVM;
    <b>constructor</b> Create(<b>const</b> JavaVM: TJavaVM; <b>const</b> URL: <b>string</b>);
    <b>procedure</b> Execute; <b>override</b>;
    <b>procedure</b> UpdateUI;
  <b>end</b>;

</code></pre></blockquote>
As you can see, it is fairly simple. There are two points here that are relevant to the discussion of threads. 
<ul>
<li>The first parameter of the constructor requires a <tt>TJavaVM</tt> object, which will be stored in the field <tt>FJavaVM</tt> when an instance of
this class is constructed. Note that a <tt>TJavaVM</tt> object is not the same as a <tt>TJNIEnv</tt> object. A <tt>TJavaVM</tt> object can be passed
between threads, but a <tt>TJNIEnv</tt> object can't.
<li>There is no field for a <tt>TJNIEnv</tt> object. That's because, as we saw earlier, you can't pass a <tt>TJNIEnv</tt> object between threads. 
(We will soon see how a thread can obtain this value if it can't be passed from one thread to another.)
</ul>
The most important aspect of a TThread-derived class is its <tt>Execute</tt> method. This is where the thread does the actual work. If you look at this
function, you will see that it is very similar to the other functions in the program, specifically, the <tt>OnClick</tt> 
event handler for <tt>btnCallObjec

⌨️ 快捷键说明

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