docs: add JNI Tips doc to the dev guide sidenav Change-Id: I7522ba8bcad96d5c424f7e894e62ceb700df3027
712 lines
33 KiB
Plaintext
712 lines
33 KiB
Plaintext
page.title=JNI Tips
|
|
@jd:body
|
|
|
|
<div id="qv-wrapper">
|
|
<div id="qv">
|
|
|
|
<h2>In this document</h2>
|
|
<ol>
|
|
<li><a href="#JavaVM_and_JNIEnv">JavaVM and JNIEnv</a></li>
|
|
<li><a href="#threads">Threads</a></li>
|
|
<li><a href="#jclass_jmethodID_and_jfieldID">jclass, jmethodID, and jfieldID</a></li>
|
|
<li><a href="#local_and_global_references">Local and Global References</a></li>
|
|
<li><a href="#UTF_8_and_UTF_16_strings">UTF-8 and UTF-16 Strings</a></li>
|
|
<li><a href="#arrays">Primitive Arrays</a></li>
|
|
<li><a href="#region_calls">Region Calls</a></li>
|
|
<li><a href="#exceptions">Exceptions</a></li>
|
|
<li><a href="#extended_checking">Extended Checking</a> </li>
|
|
<li><a href="#native_libraries">Native Libraries</a></li>
|
|
<li><a href="#64_bit">64-bit Considerations</a></li>
|
|
<li><a href="#unsupported">Unsupported Features</a></li>
|
|
<li><a href="#faq_ULE">FAQ: UnsatisfiedLinkError</a></li>
|
|
<li><a href="#faq_FindClass">FAQ: FindClass didn't find my class</a></li>
|
|
<li><a href="#faq_sharing">FAQ: Sharing raw data with native code</a></li>
|
|
</ol>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<p>JNI is the Java Native Interface. It defines a way for code written in the
|
|
Java programming language to interact with native
|
|
code, e.g. functions written in C/C++. It's VM-neutral, has support for loading code from
|
|
dynamic shared libraries, and while cumbersome at times is reasonably efficient.</p>
|
|
|
|
<p>You really should read through the
|
|
<a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html">JNI spec for J2SE 6</a>
|
|
to get a sense for how JNI works and what features are available. Some
|
|
aspects of the interface aren't immediately obvious on
|
|
first reading, so you may find the next few sections handy.
|
|
The more detailed <i>JNI Programmer's Guide and Specification</i> can be found
|
|
<a href="http://java.sun.com/docs/books/jni/html/jniTOC.html">here</a>.</p>
|
|
|
|
|
|
<a name="JavaVM_and_JNIEnv" id="JavaVM_and_JNIEnv"></a>
|
|
<h2>JavaVM and JNIEnv</h2>
|
|
|
|
<p>JNI defines two key data structures, "JavaVM" and "JNIEnv". Both of these are essentially
|
|
pointers to pointers to function tables. (In the C++ version, they're classes with a
|
|
pointer to a function table and a member function for each JNI function that indirects through
|
|
the table.) The JavaVM provides the "invocation interface" functions,
|
|
which allow you to create and destroy the VM. In theory you can have multiple VMs per process,
|
|
but Android's VM only allows one.</p>
|
|
|
|
<p>The JNIEnv provides most of the JNI functions. Your native functions all receive a JNIEnv as
|
|
the first argument.</p>
|
|
|
|
<p>On some VMs, the JNIEnv is used for thread-local storage. For this reason, <strong>you cannot share a JNIEnv between threads</strong>.
|
|
If a piece of code has no other way to get its JNIEnv, you should share
|
|
the JavaVM, and use JavaVM->GetEnv to discover the thread's JNIEnv. (Assuming it has one; see <code>AttachCurrentThread</code> below.)</p>
|
|
|
|
<p>The C declarations of JNIEnv and JavaVM are different from the C++
|
|
declarations. "jni.h" provides different typedefs
|
|
depending on whether it's included into ".c" or ".cpp". For this reason it's a bad idea to
|
|
include JNIEnv arguments in header files included by both languages. (Put another way: if your
|
|
header file requires "#ifdef __cplusplus", you may have to do some extra work if anything in
|
|
that header refers to JNIEnv.)</p>
|
|
|
|
<a name="threads" id="threads"></a>
|
|
<h2>Threads</h2>
|
|
|
|
<p>All VM threads are Linux threads, scheduled by the kernel. They're usually
|
|
started using Java language features (notably <code>Thread.start()</code>),
|
|
but they can also be created elsewhere and then attached to the VM. For
|
|
example, a thread started with <code>pthread_create</code> can be attached
|
|
with the JNI <code>AttachCurrentThread</code> or
|
|
<code>AttachCurrentThreadAsDaemon</code> functions. Until a thread is
|
|
attached to the VM, it has no JNIEnv, and
|
|
<strong>cannot make JNI calls</strong>.</p>
|
|
|
|
<p>Attaching a natively-created thread causes the VM to allocate and initialize
|
|
a <code>Thread</code> object, add it to the "main" <code>ThreadGroup</code>,
|
|
and add the thread to the set that is visible to the debugger. Calling
|
|
<code>AttachCurrentThread</code> on an already-attached thread is a no-op.</p>
|
|
|
|
<p>The Dalvik VM does not suspend threads executing native code. If
|
|
garbage collection is in progress, or the debugger has issued a suspend
|
|
request, the VM will pause the thread the next time it makes a JNI call.</p>
|
|
|
|
<p>Threads attached through JNI <strong>must call
|
|
<code>DetachCurrentThread</code> before they exit</strong>.
|
|
If coding this directly is awkward, in Android >= 2.0 ("Eclair") you
|
|
can use <code>pthread_key_create</code> to define a destructor
|
|
function that will be called before the thread exits, and
|
|
call <code>DetachCurrentThread</code> from there. (Use that
|
|
key with <code>pthread_setspecific</code> to store the JNIEnv in
|
|
thread-local-storage; that way it'll be passed into your destructor as
|
|
the argument.)</p>
|
|
|
|
|
|
<a name="jclass_jmethodID_and_jfieldID" id="jclass_jmethodID_and_jfieldID"></a>
|
|
<h2>jclass, jmethodID, and jfieldID</h2>
|
|
|
|
<p>If you want to access an object's field from native code, you would do the following:</p>
|
|
|
|
<ul>
|
|
<li> Get the class object reference for the class with <code>FindClass</code></li>
|
|
<li> Get the field ID for the field with <code>GetFieldID</code></li>
|
|
<li> Get the contents of the field with something appropriate, e.g.
|
|
<code>GetIntField</code></li>
|
|
</ul>
|
|
|
|
<p>Similarly, to call a method, you'd first get a class object reference and then a method ID. The IDs are often just
|
|
pointers to internal VM data structures. Looking them up may require several string
|
|
comparisons, but once you have them the actual call to get the field or invoke the method
|
|
is very quick.</p>
|
|
|
|
<p>If performance is important, it's useful to look the values up once and cache the results
|
|
in your native code. Because we are limiting ourselves to one VM per process, it's reasonable
|
|
to store this data in a static local structure.</p>
|
|
|
|
<p>The class references, field IDs, and method IDs are guaranteed valid until the class is unloaded. Classes
|
|
are only unloaded if all classes associated with a ClassLoader can be garbage collected,
|
|
which is rare but will not be impossible in our system. Note however that
|
|
the <code>jclass</code>
|
|
is a class reference and <strong>must be protected</strong> with a call
|
|
to <code>NewGlobalRef</code> (see the next section).</p>
|
|
|
|
<p>If you would like to cache the IDs when a class is loaded, and automatically re-cache them
|
|
if the class is ever unloaded and reloaded, the correct way to initialize
|
|
the IDs is to add a piece of code that looks like this to the appropriate class:</p>
|
|
|
|
<pre> /*
|
|
* We use a class initializer to allow the native code to cache some
|
|
* field offsets.
|
|
*/
|
|
|
|
/*
|
|
* A native function that looks up and caches interesting
|
|
* class/field/method IDs for this class. Returns false on failure.
|
|
*/
|
|
native private static boolean nativeClassInit();
|
|
|
|
/*
|
|
* Invoke the native initializer when the class is loaded.
|
|
*/
|
|
static {
|
|
if (!nativeClassInit())
|
|
throw new RuntimeException("native init failed");
|
|
}</pre>
|
|
|
|
<p>Create a nativeClassInit method in your C/C++ code that performs the ID lookups. The code
|
|
will be executed once, when the class is initialized. If the class is ever unloaded and
|
|
then reloaded, it will be executed again. (See the implementation of java.io.FileDescriptor
|
|
for an example in our source tree.)</p>
|
|
|
|
<a name="local_and_global_references" id="local_and_global_references"></a>
|
|
<h2>Local and Global References</h2>
|
|
|
|
<p>Every object that JNI returns is a "local reference". This means that it's valid for the
|
|
duration of the current native method in the current thread.
|
|
<strong>Even if the object itself continues to live on after the native method returns, the reference is not valid.</strong>
|
|
This applies to all sub-classes of <code>jobject</code>, including
|
|
<code>jclass</code>, <code>jstring</code>, and <code>jarray</code>.
|
|
(Dalvik VM will warn you about most reference mis-uses when extended JNI
|
|
checks are enabled.)</p>
|
|
|
|
<p>If you want to hold on to a reference for a longer period, you must use
|
|
a "global" reference. The <code>NewGlobalRef</code> function takes the
|
|
local reference as an argument and returns a global one.
|
|
The global reference is guaranteed to be valid until you call
|
|
<code>DeleteGlobalRef</code>.</p>
|
|
|
|
<p>This pattern is commonly used when caching copies of class objects obtained
|
|
from <code>FindClass</code>, e.g.:</p>
|
|
<pre>jclass localClass = env->FindClass("MyClass");
|
|
jclass globalClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));</pre>
|
|
|
|
<p>All JNI methods accept both local and global references as arguments.
|
|
It's possible for references to the same object to have different values;
|
|
for example, the return values from consecutive calls to
|
|
<code>NewGlobalRef</code> on the same object may be different.
|
|
<strong>To see if two references refer to the same object,
|
|
you must use the <code>IsSameObject</code> function.</strong> Never compare
|
|
references with "==" in native code.</p>
|
|
|
|
<p>One consequence of this is that you
|
|
<strong>must not assume object references are constant or unique</strong>
|
|
in native code. The 32-bit value representing an object may be different
|
|
from one invocation of a method to the next, and it's possible that two
|
|
different objects could have the same 32-bit value on consecutive calls. Do
|
|
not use <code>jobject</code> values as keys.</p>
|
|
|
|
<p>Programmers are required to "not excessively allocate" local references. In practical terms this means
|
|
that if you're creating large numbers of local references, perhaps while running through an array of
|
|
Objects, you should free them manually with
|
|
<code>DeleteLocalRef</code> instead of letting JNI do it for you. The
|
|
VM is only required to reserve slots for
|
|
16 local references, so if you need more than that you should either delete as you go or use
|
|
<code>EnsureLocalCapacity</code> to reserve more.</p>
|
|
|
|
<p>Note: method and field IDs are just 32-bit identifiers, not object
|
|
references, and should not be passed to <code>NewGlobalRef</code>. The raw data
|
|
pointers returned by functions like <code>GetStringUTFChars</code>
|
|
and <code>GetByteArrayElements</code> are also not objects.</p>
|
|
|
|
<p>One unusual case deserves separate mention. If you attach a native
|
|
thread to the VM with AttachCurrentThread, the code you are running will
|
|
never "return" to the VM until the thread detaches from the VM. Any local
|
|
references you create will have to be deleted manually unless you're going
|
|
to detach the thread soon.</p>
|
|
|
|
<a name="UTF_8_and_UTF_16_strings" id="UTF_8_and_UTF_16_strings"></a>
|
|
<h2>UTF-8 and UTF-16 Strings</h2>
|
|
|
|
<p>The Java programming language uses UTF-16. For convenience, JNI provides methods that work with <a href="http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8">Modified UTF-8</a> as well. The
|
|
modified encoding is useful for C code because it encodes \u0000 as 0xc0 0x80 instead of 0x00.
|
|
The nice thing about this is that you can count on having C-style zero-terminated strings,
|
|
suitable for use with standard libc string functions. The down side is that you cannot pass
|
|
arbitrary UTF-8 data into the VM and expect it to work correctly.</p>
|
|
|
|
<p>It's usually best to operate with UTF-16 strings. With our current VMs, the
|
|
<code>GetStringChars</code> method
|
|
does not require a copy, whereas <code>GetStringUTFChars</code> requires a malloc and a UTF conversion. Note that
|
|
<strong>UTF-16 strings are not zero-terminated</strong>, and \u0000 is allowed,
|
|
so you need to hang on to the string length as well as
|
|
the string pointer.</p>
|
|
|
|
<p><strong>Don't forget to Release the strings you Get</strong>. The
|
|
string functions return <code>jchar*</code> or <code>jbyte*</code>, which
|
|
are C-style pointers to primitive data rather than local references. They
|
|
are guaranteed valid until Release is called, which means they are not
|
|
released when the native method returns.</p>
|
|
|
|
<p><strong>Data passed to NewStringUTF must be in Modified UTF-8 format</strong>. A
|
|
common mistake is reading character data from a file or network stream
|
|
and handing it to <code>NewStringUTF</code> without filtering it.
|
|
Unless you know the data is 7-bit ASCII, you need to strip out high-ASCII
|
|
characters or convert them to proper Modified UTF-8 form. If you don't,
|
|
the UTF-16 conversion will likely not be what you expect. The extended
|
|
JNI checks will scan strings and warn you about invalid data, but they
|
|
won't catch everything.</p>
|
|
|
|
<a name="arrays" id="arrays"></a>
|
|
<h2>Primitive Arrays</h2>
|
|
|
|
<p>JNI provides functions for accessing the contents of array objects.
|
|
While arrays of objects must be accessed one entry at a time, arrays of
|
|
primitives can be read and written directly as if they were declared in C.</p>
|
|
|
|
<p>To make the interface as efficient as possible without constraining
|
|
the VM implementation,
|
|
the <code>Get<PrimitiveType>ArrayElements</code> family of calls
|
|
allows the VM to either return a pointer to the actual elements, or
|
|
allocate some memory and make a copy. Either way, the raw pointer returned
|
|
is guaranteed to be valid until the corresponding <code>Release</code> call
|
|
is issued (which implies that, if the data wasn't copied, the array object
|
|
will be pinned down and can't be relocated as part of compacting the heap).
|
|
<strong>You must Release every array you Get.</strong> Also, if the Get
|
|
call fails, you must ensure that your code doesn't try to Release a NULL
|
|
pointer later.</p>
|
|
|
|
<p>You can determine whether or not the data was copied by passing in a
|
|
non-NULL pointer for the <code>isCopy</code> argument. This is rarely
|
|
useful.</p>
|
|
|
|
<p>The <code>Release</code> call takes a <code>mode</code> argument that can
|
|
have one of three values. The actions performed by the VM depend upon
|
|
whether it returned a pointer to the actual data or a copy of it:</p>
|
|
|
|
<ul>
|
|
<li><code>0</code>
|
|
<ul>
|
|
<li>Actual: the array object is un-pinned.
|
|
<li>Copy: data is copied back. The buffer with the copy is freed.
|
|
</ul>
|
|
<li><code>JNI_COMMIT</code>
|
|
<ul>
|
|
<li>Actual: does nothing.
|
|
<li>Copy: data is copied back. The buffer with the copy
|
|
<strong>is not freed</strong>.
|
|
</ul>
|
|
<li><code>JNI_ABORT</code>
|
|
<ul>
|
|
<li>Actual: the array object is un-pinned. Earlier
|
|
writes are <strong>not</strong> aborted.
|
|
<li>Copy: the buffer with the copy is freed; any changes to it are lost.
|
|
</ul>
|
|
</ul>
|
|
|
|
<p>One reason for checking the <code>isCopy</code> flag is to know if
|
|
you need to call <code>Release</code> with <code>JNI_COMMIT</code>
|
|
after making changes to an array — if you're alternating between making
|
|
changes and executing code that uses the contents of the array, you may be
|
|
able to
|
|
skip the no-op commit. Another possible reason for checking the flag is for
|
|
efficient handling of <code>JNI_ABORT</code>. For example, you might want
|
|
to get an array, modify it in place, pass pieces to other functions, and
|
|
then discard the changes. If you know that JNI is making a new copy for
|
|
you, there's no need to create another "editable" copy. If JNI is passing
|
|
you the original, then you do need to make your own copy.</p>
|
|
|
|
<p>Some have asserted that you can skip the <code>Release</code> call if
|
|
<code>*isCopy</code> is false. This is not the case. If no copy buffer was
|
|
allocated, then the original memory must be pinned down and can't be moved by
|
|
the garbage collector.</p>
|
|
|
|
<p>Also note that the <code>JNI_COMMIT</code> flag does NOT release the array,
|
|
and you will need to call <code>Release</code> again with a different flag
|
|
eventually.</p>
|
|
|
|
|
|
<a name="region_calls" id="region_calls"></a>
|
|
<h2>Region Calls</h2>
|
|
|
|
<p>There is an alternative to calls like <code>Get<Type>ArrayElements</code>
|
|
and <code>GetStringChars</code> that may be very helpful when all you want
|
|
to do is copy data in or out. Consider the following:</p>
|
|
|
|
<pre>
|
|
jbyte* data = env->GetByteArrayElements(array, NULL);
|
|
if (data != NULL) {
|
|
memcpy(buffer, data, len);
|
|
env->ReleaseByteArrayElements(array, data, JNI_ABORT);
|
|
}</pre>
|
|
|
|
<p>This grabs the array, copies the first <code>len</code> byte
|
|
elements out of it, and then releases the array. Depending upon the VM
|
|
policies the <code>Get</code> call will either pin or copy the array contents.
|
|
We copy the data (for perhaps a second time), then call Release; in this case
|
|
we use <code>JNI_ABORT</code> so there's no chance of a third copy.</p>
|
|
|
|
<p>We can accomplish the same thing with this:</p>
|
|
<pre>
|
|
env->GetByteArrayRegion(array, 0, len, buffer);</pre>
|
|
|
|
<p>This has several advantages:</p>
|
|
<ul>
|
|
<li>Requires one JNI call instead of 2, reducing overhead.
|
|
<li>Doesn't require pinning or extra data copies.
|
|
<li>Reduces the risk of programmer error — no risk of forgetting
|
|
to call <code>Release</code> after something fails.
|
|
</ul>
|
|
|
|
<p>Similarly, you can use the <code>Set<Type>ArrayRegion</code> call
|
|
to copy data into an array, and <code>GetStringRegion</code> or
|
|
<code>GetStringUTFRegion</code> to copy characters out of a
|
|
<code>String</code>.
|
|
|
|
|
|
<a name="exceptions" id="exceptions"></a>
|
|
<h2>Exception</h2>
|
|
|
|
<p><strong>You may not call most JNI functions while an exception is pending.</strong>
|
|
Your code is expected to notice the exception (via the function's return value,
|
|
<code>ExceptionCheck()</code>, or <code>ExceptionOccurred()</code>) and return,
|
|
or clear the exception and handle it.</p>
|
|
|
|
<p>The only JNI functions that you are allowed to call while an exception is
|
|
pending are:</p>
|
|
<ul>
|
|
<li>DeleteGlobalRef
|
|
<li>DeleteLocalRef
|
|
<li>DeleteWeakGlobalRef
|
|
<li>ExceptionCheck
|
|
<li>ExceptionClear
|
|
<li>ExceptionDescribe
|
|
<li>ExceptionOccurred
|
|
<li>MonitorExit
|
|
<li>PopLocalFrame
|
|
<li>PushLocalFrame
|
|
<li>Release<PrimitiveType>ArrayElements
|
|
<li>ReleasePrimitiveArrayCritical
|
|
<li>ReleaseStringChars
|
|
<li>ReleaseStringCritical
|
|
<li>ReleaseStringUTFChars
|
|
</ul>
|
|
|
|
<p>Many JNI calls can throw an exception, but often provide a simpler way
|
|
of checking for failure. For example, if <code>NewString</code> returns
|
|
a non-NULL value, you don't need to check for an exception. However, if
|
|
you call a method (using a function like <code>CallObjectMethod</code>),
|
|
you must always check for an exception, because the return value is not
|
|
going to be valid if an exception was thrown.</p>
|
|
|
|
<p>Note that exceptions thrown by interpreted code do not "leap over" native code,
|
|
and C++ exceptions thrown by native code are not handled by Dalvik.
|
|
The JNI <code>Throw</code> and <code>ThrowNew</code> instructions just
|
|
set an exception pointer in the current thread. Upon returning to the VM from
|
|
native code, the exception will be noted and handled appropriately.</p>
|
|
|
|
<p>Native code can "catch" an exception by calling <code>ExceptionCheck</code> or
|
|
<code>ExceptionOccurred</code>, and clear it with
|
|
<code>ExceptionClear</code>. As usual,
|
|
discarding exceptions without handling them can lead to problems.</p>
|
|
|
|
<p>There are no built-in functions for manipulating the Throwable object
|
|
itself, so if you want to (say) get the exception string you will need to
|
|
find the Throwable class, look up the method ID for
|
|
<code>getMessage "()Ljava/lang/String;"</code>, invoke it, and if the result
|
|
is non-NULL use <code>GetStringUTFChars</code> to get something you can
|
|
hand to printf or a LOG macro.</p>
|
|
|
|
|
|
<a name="extended_checking" id="extended_checking"></a>
|
|
<h2>Extended Checking</h2>
|
|
|
|
<p>JNI does very little error checking. Calling <code>SetIntField</code>
|
|
on an Object field will succeed, even if the field is marked
|
|
<code>private</code> and <code>final</code>. The
|
|
goal is to minimize the overhead on the assumption that, if you've written it in native code,
|
|
you probably did it for performance reasons.</p>
|
|
|
|
<p>In Dalvik, you can enable additional checks by setting the
|
|
"<code>-Xcheck:jni</code>" flag. If the flag is set, the VM directs
|
|
the JavaVM and JNIEnv pointers to a different table of functions.
|
|
These functions perform an extended series of checks before calling the
|
|
standard implementation.</p>
|
|
|
|
<p>The additional tests include:</p>
|
|
|
|
<ul>
|
|
<li> Check for null pointers where not allowed.</li>
|
|
<li> Verify argument type correctness (jclass is a class object,
|
|
jfieldID points to field data, jstring is a java.lang.String).</li>
|
|
<li> Field type correctness, e.g. don't store a HashMap in a String field.</li>
|
|
<li> Ensure jmethodID is appropriate when making a static or virtual
|
|
method call.</li>
|
|
<li> Check to see if an exception is pending on calls where pending exceptions are not legal.</li>
|
|
<li> Check for calls to inappropriate functions between Critical get/release calls.</li>
|
|
<li> Check that JNIEnv structs aren't being shared between threads.</li>
|
|
<li> Make sure local references aren't used outside their allowed lifespan.</li>
|
|
<li> UTF-8 strings contain only valid <a href="http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8">Modified UTF-8</a> data.</li>
|
|
</ul>
|
|
|
|
<p>Accessibility of methods and fields (i.e. public vs. private) is not
|
|
checked.</p>
|
|
|
|
<p>For a description of how to enable CheckJNI for Android apps, see
|
|
<a href="embedded-vm-control.html">Controlling the Embedded VM</a>.
|
|
It's currently enabled by default in the Android emulator and on
|
|
"engineering" device builds.</p>
|
|
|
|
<p>JNI checks can be modified with the <code>-Xjniopts</code> command-line
|
|
flag. Currently supported values include:</p>
|
|
|
|
<dl>
|
|
<dt>forcecopy
|
|
<dd>When set, any function that can return a copy of the original data
|
|
(array of primitive values, UTF-16 chars) will always do so. The buffers
|
|
are over-allocated and surrounded with a guard pattern to help identify
|
|
code writing outside the buffer, and the contents are erased before the
|
|
storage is freed to trip up code that uses the data after calling Release.
|
|
This will have a noticeable performance impact on some applications.
|
|
<dt>warnonly
|
|
<dd>By default, JNI "warnings" cause the VM to abort. With this flag
|
|
it continues on.
|
|
</dl>
|
|
|
|
|
|
<a name="native_libraries" id="native_libraries"></a>
|
|
<h2>Native Libraries</h2>
|
|
|
|
<p>You can load native code from shared libraries with the standard
|
|
<code>System.loadLibrary()</code> call. The
|
|
preferred way to get at your native code is:</p>
|
|
|
|
<ul>
|
|
<li> Call <code>System.loadLibrary()</code> from a static class
|
|
initializer. (See the earlier example, where one is used to call
|
|
<code>nativeClassInit()</code>.) The argument is the "undecorated"
|
|
library name, e.g. to load "libfubar.so" you would pass in "fubar".</li>
|
|
<li> Provide a native function: <code><strong>jint JNI_OnLoad(JavaVM* vm, void* reserved)</strong></code></li>
|
|
<li>In <code>JNI_OnLoad</code>, register all of your native methods. You
|
|
should declare
|
|
the functions <code>static</code> so the names don't take up space in the symbol table
|
|
on the device.</li>
|
|
</ul>
|
|
|
|
<p>The <code>JNI_OnLoad</code> function should look something like this if
|
|
written in C++:</p>
|
|
<pre>jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
|
{
|
|
JNIEnv* env;
|
|
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
|
|
return -1;
|
|
}
|
|
|
|
// Get jclass with env->FindClass.
|
|
// Register methods with env->RegisterNatives.
|
|
|
|
return JNI_VERSION_1_6;
|
|
}</pre>
|
|
|
|
<p>You can also call <code>System.load()</code> with the full path name of the
|
|
shared library. For Android apps, you may find it useful to get the full
|
|
path to the application's private data storage area from the context object.</p>
|
|
|
|
<p>This is the recommended approach, but not the only approach. The VM does
|
|
not require explicit registration, nor that you provide a
|
|
<code>JNI_OnLoad</code> function.
|
|
You can instead use "discovery" of native methods that are named in a
|
|
specific way (see <a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/design.html#wp615">
|
|
the JNI spec</a> for details), though this is less desirable.
|
|
It requires more space in the shared object symbol table,
|
|
loading is slower because it requires string searches through all of the
|
|
loaded shared libraries, and if a method signature is wrong you won't know
|
|
about it until the first time the method is actually used.</p>
|
|
|
|
<p>One other note about <code>JNI_OnLoad</code>: any <code>FindClass</code>
|
|
calls you make from there will happen in the context of the class loader
|
|
that was used to load the shared library. Normally <code>FindClass</code>
|
|
uses the loader associated with the method at the top of the interpreted
|
|
stack, or if there isn't one (because the thread was just attached to
|
|
the VM) it uses the "system" class loader. This makes
|
|
<code>JNI_OnLoad</code> a convenient place to look up and cache class
|
|
object references.</p>
|
|
|
|
|
|
<a name="64_bit" id="64_bit"></a>
|
|
<h2>64-bit Considerations</h2>
|
|
|
|
<p>Android is currently expected to run on 32-bit platforms. In theory it
|
|
could be built for a 64-bit system, but that is not a goal at this time.
|
|
For the most part this isn't something that you will need to worry about
|
|
when interacting with native code,
|
|
but it becomes significant if you plan to store pointers to native
|
|
structures in integer fields in an object. To support architectures
|
|
that use 64-bit pointers, <strong>you need to stash your native pointers in a
|
|
<code>long</code> field rather than an <code>int</code></strong>.
|
|
|
|
|
|
<a name="unsupported" id="unsupported"></a>
|
|
<h2>Unsupported Features</h2>
|
|
|
|
<p>All JNI 1.6 features are supported, with the following exceptions:</p>
|
|
<ul>
|
|
<li><code>DefineClass</code> is not implemented. Dalvik does not use
|
|
Java bytecodes or class files, so passing in binary class data
|
|
doesn't work. Translation facilities may be added in a future
|
|
version of the VM.</li>
|
|
<li>"Weak global" references are implemented, but may only be passed
|
|
to <code>NewLocalRef</code>, <code>NewGlobalRef</code>, and
|
|
<code>DeleteWeakGlobalRef</code>. (The spec strongly encourages
|
|
programmers to create hard references to weak globals before doing
|
|
anything with them, so this should not be at all limiting.)</li>
|
|
<li><code>GetObjectRefType</code> (new in JNI 1.6) is implemented but not fully
|
|
functional — it can't always tell the difference between "local" and
|
|
"global" references.</li>
|
|
</ul>
|
|
|
|
<p>For backward compatibility, you may need to be aware of:</p>
|
|
<ul>
|
|
<li>Until Android 2.0 ("Eclair"), the '$' character was not properly
|
|
converted to "_00024" during searches for method names. Working
|
|
around this requires using explicit registration or moving the
|
|
native methods out of inner classes.
|
|
<li>Until Android 2.0 ("Eclair"), it was not possible to use a <code>pthread_key_create</code>
|
|
destructor function to avoid the VM's "thread must be detached before
|
|
exit" check. (The VM also uses a pthread key destructor function,
|
|
so it'd be a race to see which gets called first.)
|
|
<li>"Weak global" references were not implemented until Android 2.2 ("Froyo").
|
|
Older VMs will vigorously reject attempts to use them. You can use
|
|
the Android platform version constants to test for support.
|
|
</ul>
|
|
|
|
|
|
<a name="faq_ULE" id="faq_ULE"></a>
|
|
<h2>FAQ: UnsatisfiedLinkError</h2>
|
|
|
|
<p>When working on native code it's not uncommon to see a failure like this:</p>
|
|
<pre>java.lang.UnsatisfiedLinkError: Library foo not found</pre>
|
|
|
|
<p>In some cases it means what it says — the library wasn't found. In
|
|
other cases the library exists but couldn't be opened by dlopen(), and
|
|
the details of the failure can be found in the exception's detail message.</p>
|
|
|
|
<p>Common reasons why you might encounter "library not found" exceptions:</p>
|
|
<ul>
|
|
<li>The library doesn't exist or isn't accessible to the app. Use
|
|
<code>adb shell ls -l <path></code> to check its presence
|
|
and permissions.
|
|
<li>The library wasn't built with the NDK. This can result in
|
|
dependencies on functions or libraries that don't exist on the device.
|
|
</ul>
|
|
|
|
<p>Another class of <code>UnsatisfiedLinkError</code> failures looks like:</p>
|
|
<pre>java.lang.UnsatisfiedLinkError: myfunc
|
|
at Foo.myfunc(Native Method)
|
|
at Foo.main(Foo.java:10)</pre>
|
|
|
|
<p>In logcat, you'll see:</p>
|
|
<pre>W/dalvikvm( 880): No implementation found for native LFoo;.myfunc ()V</pre>
|
|
|
|
<p>This means that the VM tried to find a matching method but was unsuccessful.
|
|
Some common reasons for this are:</p>
|
|
<ul>
|
|
<li>The library isn't getting loaded. Check the logcat output for
|
|
messages about library loading.
|
|
<li>The method isn't being found due to a name or signature mismatch. This
|
|
is commonly caused by:
|
|
<ul>
|
|
<li>For lazy method lookup, failing to declare C++ functions
|
|
with <code>extern "C"</code>. You can use <code>arm-eabi-nm</code>
|
|
to see the symbols as they appear in the library; if they look
|
|
mangled (e.g. <code>_Z15Java_Foo_myfuncP7_JNIEnvP7_jclass</code>
|
|
rather than <code>Java_Foo_myfunc</code>) then you need to
|
|
adjust the declaration.
|
|
<li>For explicit registration, minor errors when entering the
|
|
method signature. Make sure that what you're passing to the
|
|
registration call matches the signature in the log file.
|
|
Remember that 'B' is <code>byte</code> and 'Z' is <code>boolean</code>.
|
|
Class name components in signatures start with 'L', end with ';',
|
|
use '/' to separate package/class names, and use '$' to separate
|
|
inner-class names
|
|
(e.g. <code>Ljava/util/Map$Entry;</code>).
|
|
</ul>
|
|
</ul>
|
|
|
|
<p>Using <code>javah</code> to automatically generate JNI headers may help
|
|
avoid some problems.
|
|
|
|
|
|
<a name="faq_FindClass" id="faq_FindClass"></a>
|
|
<h2>FAQ: FindClass didn't find my class</h2>
|
|
|
|
<p>Make sure that the class name string has the correct format. JNI class
|
|
names start with the package name and are separated with slashes,
|
|
e.g. <code>java/lang/String</code>. If you're looking up an array class,
|
|
you need to start with the appropriate number of square brackets and
|
|
must also wrap the class with 'L' and ';', so a one-dimensional array of
|
|
<code>String</code> would be <code>[Ljava/lang/String;</code>.</p>
|
|
|
|
<p>If the class name looks right, you could be running into a class loader
|
|
issue. <code>FindClass</code> wants to start the class search in the
|
|
class loader associated with your code. It examines the VM call stack,
|
|
which will look something like:
|
|
<pre> Foo.myfunc(Native Method)
|
|
Foo.main(Foo.java:10)
|
|
dalvik.system.NativeStart.main(Native Method)</pre>
|
|
|
|
<p>The topmost method is <code>Foo.myfunc</code>. <code>FindClass</code>
|
|
finds the <code>ClassLoader</code> object associated with the <code>Foo</code>
|
|
class and uses that.</p>
|
|
|
|
<p>This usually does what you want. You can get into trouble if you
|
|
create a thread outside the VM (perhaps by calling <code>pthread_create</code>
|
|
and then attaching it to the VM with <code>AttachCurrentThread</code>).
|
|
Now the stack trace looks like this:</p>
|
|
<pre> dalvik.system.NativeStart.run(Native Method)</pre>
|
|
|
|
<p>The topmost method is <code>NativeStart.run</code>, which isn't part of
|
|
your application. If you call <code>FindClass</code> from this thread, the
|
|
VM will start in the "system" class loader instead of the one associated
|
|
with your application, so attempts to find app-specific classes will fail.</p>
|
|
|
|
<p>There are a few ways to work around this:</p>
|
|
<ul>
|
|
<li>Do your <code>FindClass</code> lookups once, in
|
|
<code>JNI_OnLoad</code>, and cache the class references for later
|
|
use. Any <code>FindClass</code> calls made as part of executing
|
|
<code>JNI_OnLoad</code> will use the class loader associated with
|
|
the function that called <code>System.loadLibrary</code> (this is a
|
|
special rule, provided to make library initialization more convenient).
|
|
If your app code is loading the library, <code>FindClass</code>
|
|
will use the correct class loader.
|
|
<li>Pass an instance of the class into the functions that need
|
|
it, e.g. declare your native method to take a Class argument and
|
|
then pass <code>Foo.class</code> in.
|
|
<li>Cache a reference to the <code>ClassLoader</code> object somewhere
|
|
handy, and issue <code>loadClass</code> calls directly. This requires
|
|
some effort.
|
|
</ul>
|
|
|
|
|
|
<a name="faq_sharing" id="faq_sharing"></a>
|
|
<h2>FAQ: Sharing raw data with native code</h2>
|
|
|
|
<p>You may find yourself in a situation where you need to access a large
|
|
buffer of raw data from code written in Java and C/C++. Common examples
|
|
include manipulation of bitmaps or sound samples. There are two
|
|
basic approaches.</p>
|
|
|
|
<p>You can store the data in a <code>byte[]</code>. This allows very fast
|
|
access from code written in Java. On the native side, however, you're
|
|
not guaranteed to be able to access the data without having to copy it. In
|
|
some implementations, <code>GetByteArrayElements</code> and
|
|
<code>GetPrimitiveArrayCritical</code> will return actual pointers to the
|
|
raw data in the managed heap, but in others it will allocate a buffer
|
|
on the native heap and copy the data over.</p>
|
|
|
|
<p>The alternative is to store the data in a direct byte buffer. These
|
|
can be created with <code>java.nio.ByteBuffer.allocateDirect</code>, or
|
|
the JNI <code>NewDirectByteBuffer</code> function. Unlike regular
|
|
byte buffers, the storage is not allocated on the managed heap, and can
|
|
always be accessed directly from native code (get the address
|
|
with <code>GetDirectBufferAddress</code>). Depending on how direct
|
|
byte buffer access is implemented in the VM, accessing the data from code
|
|
written in Java can be very slow.</p>
|
|
|
|
<p>The choice of which to use depends on two factors:</p>
|
|
<ol>
|
|
<li>Will most of the data accesses happen from code written in Java
|
|
or in C/C++?
|
|
<li>If the data is eventually being passed to a system API, what form
|
|
must it be in? (For example, if the data is eventually passed to a
|
|
function that takes a byte[], doing processing in a direct
|
|
<code>ByteBuffer</code> might be unwise.)
|
|
</ol>
|
|
|
|
<p>If there's no clear winner, use a direct byte buffer. Support for them
|
|
is built directly into JNI, and access to them from code written in
|
|
Java can be made faster with VM improvements.</p>
|