<li> <a href="#What_s_JNI_">What's JNI?</a>
</li>
<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>
<li> <a href="#Unsupported">Unsupported Features</a>
+</li>
+
+<li> <a href="#FAQUnsatisfied">FAQ: UnsatisfiedLinkError</a>
+</li>
+<li> <a href="#FAQFindClass">FAQ: FindClass didn't find my class</a>
+</li>
+<li> <a href="#FAQSharing">FAQ: Sharing raw data with native code</a>
+</li>
+
</ul>
<p>
<noautolink>
pointers to pointers to function tables. (In the C++ version, it's a class whose sole member
is a pointer to a function 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 VMs only allow one.
+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.
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.
</p><p>
-The C and C++ declarations of JNIEnv and JavaVM are different. "jni.h" provides different typedefs
+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><p>
-</p><p>
+</p><h2><a name="Threads"> Threads </a></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 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><h2><a name="jclass_jmethodID_and_jfieldID"> jclass, jmethodID, and jfieldID </a></h2>
<p>
If you want to access an object's field from native code, you would do the following:
* 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.
*/
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><p>
</p><p>
</p><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
+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
env->GetByteArrayRegion(array, 0, len, buffer);
</pre>
</p><p>
-This accomplishes the same thing, with several advantages:
+This has several advantages:
<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
+ <li>Reduces the risk of programmer error — no risk of forgetting
to call <code>Release</code> after something fails.
</ul>
</p><p>
<li>ReleaseStringUTFChars
</ul></font>
</p><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
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>
-Some VMs support extended checking with the "<code>-Xcheck:jni</code>" flag. If the flag is set, the VM
-puts a different table of functions into the JavaVM and JNIEnv pointers. These functions do
-an extended series of checks before calling the standard implementation.
+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>
-Some things that may be checked:
+The additional tests include:
</p><p>
</p>
<ul>
<li> Check for null pointers where not allowed.
-<li>
+</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> Make sure local references aren't used outside their allowed lifespan.
</li>
-<li> UTF-8 strings contain valid "modified UTF-8" data.
+<li> UTF-8 strings contain only valid "modified UTF-8" data.
</li>
</ul>
<p>Accessibility of methods and fields (i.e. public vs. private) is not
checked.
<p>
-The Dalvik VM supports the <code>-Xcheck:jni</code> flag. For a
-description of how to enable it for Android apps, see
+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><blockquote><pre>jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env;
- if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
+ if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK)
return -1;
/* get class with (*env)->FindClass */
/* register methods with (*env)->RegisterNatives */
- return JNI_VERSION_1_4;
+ return JNI_VERSION_1_6;
}
</pre></blockquote>
</p><p>
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.
+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><p>
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 1.6) is implemented but not fully
- functional -- it can't always tell the difference between "local" and
+ 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:
+<ul>
+ <li>Until 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 2.0, 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 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>
+
+
+</p><h2><a name="FAQUnsatisfied"> FAQ: UnsatisfiedLinkError </a></h2>
+<p>
+When working on native code it's not uncommon to see a failure like this:
+<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>
+Common reasons why you might encounter "library not found" exceptions:
+<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><p>
+Another class of <code>UnsatisfiedLinkError</code> failures looks like:
+<pre>java.lang.UnsatisfiedLinkError: myfunc
+ at Foo.myfunc(Native Method)
+ at Foo.main(Foo.java:10)</pre>
+<p>
+In logcat, you'll see:
+<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:
+<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.
+
+
+</p><h2><a name="FAQFindClass"> FAQ: FindClass didn't find my class </a></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:
+<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:
+<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>
+
+</p><p>
+
+
+</p><h2><a name="FAQSharing"> FAQ: Sharing raw data with native code </a></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:
+<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>
+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>
<address>Copyright © 2008 The Android Open Source Project</address>