📄 tij0191.html
字号:
<font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">void</font> main(String [] args) {
UseObjects app = <font color="#0000ff">new</font> UseObjects();
MyJavaClass anObj = <font color="#0000ff">new</font> MyJavaClass();
anObj.aValue = 2;
app.changeObject(anObj);
System.out.println("Java: " + anObj.aValue);
}
<font color="#0000ff">private</font> <font color="#0000ff">native</font> <font color="#0000ff">void</font>
changeObject(MyJavaClass obj);
<font color="#0000ff">static</font> {
System.loadLibrary("UseObjImpl");
}
}</PRE></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">After
compiling the code and handing the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>.class
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">file
to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>javah</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
you can implement the native method. In the example below, once the field and
method ID are obtained, they are accessed through JNI functions.
</FONT><P></DIV>
<font color="#990000"><PRE>JNIEXPORT <font color="#0000ff">void</font> JNICALL
Java_UseObjects_changeObject(
JNIEnv * env, jobject jThis, jobject obj) {
jclass cls;
jfieldID fid;
jmethodID mid;
<font color="#0000ff">int</font> value;
cls = env->GetObjectClass(obj);
fid = env->GetFieldID(cls,
"aValue", "I");
mid = env->GetMethodID(cls,
"divByTwo", "()V");
value = env->GetIntField(obj, fid);
printf("Native: %d\n", value);
env->SetIntField(obj, fid, 6);
env->CallVoidMethod(obj, mid);
value = env->GetIntField(obj, fid);
printf("Native: %d\n", value);
}</PRE></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
first argument aside, the C++ function receives a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>jobject</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
which is the native side of the Java object reference we pass from the Java
code. We simply read
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>aValue</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
print it out, change the value, call the object’s
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>divByTwo( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method, and print the value out again.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">To
access a field or method, you must first obtain its identifier. Appropriate JNI
functions take the class object, the element name, and the signature. These
functions return an identifier that you use to access the element. This
approach might seem convoluted, but your native method has no knowledge of the
internal layout of the Java object. Instead, it must access fields and methods
through indexes returned by the JVM. This allows different JVMs to implement
different internal object layouts with no impact on your native methods.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">If
you run the Java program, you’ll see that the object that’s passed
from the Java side is manipulated by your native method. But what exactly is
passed? A pointer or a Java reference? And what is the garbage collector doing
during native method calls?
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
<A NAME="Index3097"></A>garbage
collector continues to operate during native method execution, but it’s
guaranteed that your objects will not be garbage collected during a native
method call. To ensure this,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>local
references
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
are created before, and destroyed right after, the native method call. Since
their lifetime wraps the call, you know that the objects will be valid
throughout the native method call.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Since
these references are created and subsequently destroyed every time the function
is called, you cannot make local copies in your native methods, in
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>static</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
variables. If you want a reference that lasts across function invocations, you
need a global reference. Global references are not created by the JVM, but the
programmer can make a global reference out of a local one by calling specific
JNI functions. When you create a global reference, you become responsible for
the lifetime of the referenced object. The global reference (and the object it
refers to) will be in memory until the programmer explicitly frees the
reference with the appropriate JNI function. It’s similar to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>malloc( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>free( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
in C.
</FONT><a name="_Toc408018822"></a><P></DIV>
<A NAME="Heading593"></A><H3 ALIGN=LEFT>
JNI
and Java exceptions
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">With
<A NAME="Index3098"></A>JNI,
Java exceptions can be thrown, caught, printed, and rethrown just as they are
inside a Java program. But it’s up to the programmer to call dedicated
JNI functions to deal with exceptions. Here are the JNI functions for exception
handling:
</FONT><P></DIV>
<UL>
<LI><FONT FACE="Symbol" SIZE=3 COLOR="Black"> </FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Throw( )</B></FONT><P><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Throws
an existing exception object. Used in native methods to rethrow an exception.
</FONT><LI><FONT FACE="Symbol" SIZE=3 COLOR="Black"> </FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>ThrowNew( )</B></FONT><P><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Generates
a new exception object and throws it.
</FONT><LI><FONT FACE="Symbol" SIZE=3 COLOR="Black"> </FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>ExceptionOccurred( )</B></FONT><P><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Determines
if an exception was thrown and not yet cleared.
</FONT><LI><FONT FACE="Symbol" SIZE=3 COLOR="Black"> </FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>ExceptionDescribe( )</B></FONT><P><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Prints
an exception and the stack trace.
</FONT><LI><FONT FACE="Symbol" SIZE=3 COLOR="Black"> </FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>ExceptionClear( )</B></FONT><P><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Clears
a pending exception.
</FONT><LI><FONT FACE="Symbol" SIZE=3 COLOR="Black"> </FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>FatalError( )</B></FONT><P><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Raises
a fatal error. Does not return.
</FONT></UL><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Among
these, you can’t ignore
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>ExceptionOccurred( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>ExceptionClear( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
Most JNI functions can generate exceptions, and there is no language feature
that you can use in place of a Java try block, so you must call
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>ExceptionOccurred( )
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">after
each JNI function call to see if an exception was thrown. If you detect an
exception, you may choose to handle it (and possibly rethrow it). You must make
certain, however, that the exception is eventually cleared. This can be done in
your function using
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>ExceptionClear( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
or in some other function if the exception is rethrown, but it must be done.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">You
must ensure that the exception is cleared, because otherwise the results will
be unpredictable if you call a JNI function while an exception is pending.
There are few JNI functions that are safe to call during an exception; among
these, of course, are all the exception handling functions.
</FONT><a name="_Toc408018823"></a><P></DIV>
<A NAME="Heading594"></A><H3 ALIGN=LEFT>
JNI
and threading
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Since
Java is a multithreaded language, several threads can call a native method
concurrently. (The native method might be suspended in the middle of its
operation when a second thread calls it.) It’s entirely up to the
programmer to guarantee that the native call is thread-safe, i.e. it does not
modify shared data in an unmonitored way. Basically, you have two options:
declare the native method as
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>synchronized</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
or implement some other strategy within the native method to ensure correct,
concurrent data manipulation.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Also,
you should never pass the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>JNIEnv</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
pointer across threads, since the internal structure it points to is allocated
on a per-thread basis and contains information that makes sense only in that
particular thread.
</FONT><a name="_Toc408018824"></a><P></DIV>
<A NAME="Heading595"></A><H3 ALIGN=LEFT>
Using
a pre-existing code base
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
easiest way to implement JNI native methods is to start writing native method
prototypes in a Java class, compile that class, and run the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>.class</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
file through
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>javah</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
But what if you have a large, pre-existing code base that you want to call from
Java? Renaming all the functions in your DLLs to match the JNI name mangling
convention is not a viable solution. The best approach is to write a wrapper
DLL “outside” your original code base. The Java code calls
functions in this new DLL, which in turn calls your original DLL functions.
This solution is not just a work-around; in most cases you must do this anyway
because you must call JNI functions on the object references before you can use
them.
</FONT><a name="_Toc408018825"></a><P></DIV>
<div align="right">
<a href="tij_c.html">Contents</a> | <a href="tij0190.html">Prev</a> | <a href="tij0192.html">Next</a>
</div>
</body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -