| // Copyright 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.example.jni_generator; |
| |
| import android.graphics.Rect; |
| |
| import org.chromium.base.AccessedByNative; |
| import org.chromium.base.CalledByNative; |
| import org.chromium.base.CalledByNativeUnchecked; |
| import org.chromium.base.JNINamespace; |
| import org.chromium.base.NativeClassQualifiedName; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| // This class serves as a reference test for the bindings generator, and as example documentation |
| // for how to use the jni generator. |
| // The C++ counter-part is sample_for_tests.cc. |
| // jni_generator.gyp has a jni_generator_tests target that will: |
| // * Generate a header file for the JNI bindings based on this file. |
| // * Compile sample_for_tests.cc using the generated header file. |
| // * link a native executable to prove the generated header + cc file are self-contained. |
| // All comments are informational only, and are ignored by the jni generator. |
| // |
| // Binding C/C++ with Java is not trivial, specially when ownership and object lifetime |
| // semantics needs to be managed across boundaries. |
| // Following a few guidelines will make the code simpler and less buggy: |
| // |
| // - Never write any JNI "by hand". Rely on the bindings generator to have a thin |
| // layer of type-safety. |
| // |
| // - Treat the types from the other side as "opaque" as possible. Do not inspect any |
| // object directly, but rather, rely on well-defined getters / setters. |
| // |
| // - Minimize the surface API between the two sides, and rather than calling multiple |
| // functions across boundaries, call only one (and then, internally in the other side, |
| // call as many little functions as required). |
| // |
| // - If a Java object "owns" a native object, stash the pointer in a "long mNativeClassName". |
| // Note that it needs to have a "destruction path", i.e., it must eventually call a method |
| // to delete the native object (for example, the java object has a "close()" method that |
| // in turn deletes the native object). Avoid relying on finalizers: those run in a different |
| // thread and makes the native lifetime management more difficult. |
| // |
| // - For native object "owning" java objects: |
| // - If there's a strong 1:1 to relationship between native and java, the best way is to |
| // stash the java object into a base::android::ScopedJavaGlobalRef. This will ensure the |
| // java object can be GC'd once the native object is destroyed but note that this global strong |
| // ref implies a new GC root, so be sure it will not leak and it must never rely on being |
| // triggered (transitively) from a java side GC. |
| // - In all other cases, the native side should keep a JavaObjectWeakGlobalRef, and check whether |
| // that reference is still valid before de-referencing it. Note that you will need another |
| // java-side object to be holding a strong reference to this java object while it is in use, to |
| // avoid unpredictable GC of the object before native side has finished with it. |
| // |
| // - The best way to pass "compound" datatypes across in either direction is to create an inner |
| // class with PODs and a factory function. If possible, make it immutable (i.e., mark all the |
| // fields as "final"). See examples with "InnerStructB" below. |
| // |
| // - It's simpler to create thin wrappers with a well defined JNI interface than to |
| // expose a lot of internal details. This is specially significant for system classes where it's |
| // simpler to wrap factory methods and a few getters / setters than expose the entire class. |
| // |
| // - Use static factory functions annotated with @CalledByNative rather than calling the |
| // constructors directly. |
| // |
| // - Iterate over containers where they are originally owned, then create inner structs or |
| // directly call methods on the other side. It's much simpler than trying to amalgamate |
| // java and stl containers. |
| // |
| // This JNINamespace annotation indicates that all native methods should be |
| // generated inside this namespace, including the native class that this |
| // object binds to. |
| @JNINamespace("base::android") |
| class SampleForTests { |
| // Classes can store their C++ pointer counter part as an int that is normally initialized by |
| // calling out a nativeInit() function. |
| long mNativeCPPObject; |
| |
| // You can define methods and attributes on the java class just like any other. |
| // Methods without the @CalledByNative annotation won't be exposed to JNI. |
| public SampleForTests() { |
| } |
| |
| public void startExample() { |
| // Calls native code and holds a pointer to the C++ class. |
| mNativeCPPObject = nativeInit("myParam"); |
| } |
| |
| public void doStuff() { |
| // This will call CPPClass::Method() using nativePtr as a pointer to the object. This must be |
| // done to: |
| // * avoid leaks. |
| // * using finalizers are not allowed to destroy the cpp class. |
| nativeMethod(mNativeCPPObject); |
| } |
| |
| public void finishExample() { |
| // We're done, so let's destroy nativePtr object. |
| nativeDestroy(mNativeCPPObject); |
| } |
| |
| // ----------------------------------------------------------------------------------------------- |
| // The following methods demonstrate exporting Java methods for invocation from C++ code. |
| // Java functions are mapping into C global functions by prefixing the method name with |
| // "Java_<Class>_" |
| // This is triggered by the @CalledByNative annotation; the methods may be named as you wish. |
| |
| // Exported to C++ as: |
| // Java_Example_javaMethod(JNIEnv* env, jobject obj, jint foo, jint bar) |
| // Typically the C++ code would have obtained the jobject via the Init() call described above. |
| @CalledByNative |
| public int javaMethod(int foo, |
| int bar) { |
| return 0; |
| } |
| |
| // Exported to C++ as Java_Example_staticJavaMethod(JNIEnv* env) |
| // Note no jobject argument, as it is static. |
| @CalledByNative |
| public static boolean staticJavaMethod() { |
| return true; |
| } |
| |
| // No prefix, so this method is package private. It will still be exported. |
| @CalledByNative |
| void packagePrivateJavaMethod() {} |
| |
| // Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that |
| // call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to |
| // call ClearException() and act as appropriate. |
| // See more details at the "@CalledByNativeUnchecked" annotation. |
| @CalledByNativeUnchecked |
| void methodThatThrowsException() throws Exception {} |
| |
| // The generator is not confused by inline comments: |
| // @CalledByNative void thisShouldNotAppearInTheOutput(); |
| // @CalledByNativeUnchecked public static void neitherShouldThis(int foo); |
| |
| /** |
| * The generator is not confused by block comments: |
| * @CalledByNative void thisShouldNotAppearInTheOutputEither(); |
| * @CalledByNativeUnchecked public static void andDefinitelyNotThis(int foo); |
| */ |
| |
| // String constants that look like comments don't confuse the generator: |
| private String arrgh = "*/*"; |
| |
| //------------------------------------------------------------------------------------------------ |
| // Java fields which are accessed from C++ code only must be annotated with @AccessedByNative to |
| // prevent them being eliminated when unreferenced code is stripped. |
| @AccessedByNative |
| private int javaField; |
| |
| //------------------------------------------------------------------------------------------------ |
| // The following methods demonstrate declaring methods to call into C++ from Java. |
| // The generator detects the "native" and "static" keywords, the type and name of the first |
| // parameter, and the "native" prefix to the function name to determine the C++ function |
| // signatures. Besides these constraints the methods can be freely named. |
| |
| // This declares a C++ function which the application code must implement: |
| // static jint Init(JNIEnv* env, jobject obj); |
| // The jobject parameter refers back to this java side object instance. |
| // The implementation must return the pointer to the C++ object cast to jint. |
| // The caller of this method should store it, and supply it as a the nativeCPPClass param to |
| // subsequent native method calls (see the methods below that take an "int native..." as first |
| // param). |
| private native long nativeInit(String param); |
| |
| // This defines a function binding to the associated C++ class member function. The name is |
| // derived from |nativeDestroy| and |nativeCPPClass| to arrive at CPPClass::Destroy() (i.e. native |
| // prefixes stripped). |
| // The |nativeCPPClass| is automatically cast to type CPPClass* in order to obtain the object on |
| // which to invoke the member function. |
| private native void nativeDestroy(long nativeCPPClass); |
| |
| // This declares a C++ function which the application code must implement: |
| // static jdouble GetDoubleFunction(JNIEnv* env, jobject obj); |
| // The jobject parameter refers back to this java side object instance. |
| private native double nativeGetDoubleFunction(); |
| |
| // Similar to nativeGetDoubleFunction(), but here the C++ side will receive a jclass rather than |
| // jobject param, as the function is declared static. |
| private static native float nativeGetFloatFunction(); |
| |
| // This function takes a non-POD datatype. We have a list mapping them to their full classpath in |
| // jni_generator.py JavaParamToJni. If you require a new datatype, make sure you add to that |
| // function. |
| private native void nativeSetNonPODDatatype(Rect rect); |
| |
| // This declares a C++ function which the application code must implement: |
| // static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, jobject obj); |
| // The jobject parameter refers back to this java side object instance. |
| // Note that it returns a ScopedJavaLocalRef<jobject> so that you don' have to worry about |
| // deleting the JNI local reference. This is similar with Strings and arrays. |
| private native Object nativeGetNonPODDatatype(); |
| |
| // Similar to nativeDestroy above, this will cast nativeCPPClass into pointer of CPPClass type and |
| // call its Method member function. |
| private native int nativeMethod(long nativeCPPClass); |
| |
| // Similar to nativeMethod above, but here the C++ fully qualified class name is taken from the |
| // annotation rather than parameter name, which can thus be chosen freely. |
| @NativeClassQualifiedName("CPPClass::InnerClass") |
| private native double nativeMethodOtherP0(long nativePtr); |
| |
| // This "struct" will be created by the native side using |createInnerStructA|, |
| // and used by the java-side somehow. |
| // Note that |@CalledByNative| has to contain the inner class name. |
| static class InnerStructA { |
| private final long mLong; |
| private final int mInt; |
| private final String mString; |
| |
| private InnerStructA(long l, int i, String s) { |
| mLong = l; |
| mInt = i; |
| mString = s; |
| } |
| |
| @CalledByNative("InnerStructA") |
| private static InnerStructA create(long l, int i, String s) { |
| return new InnerStructA(l, i, s); |
| } |
| } |
| |
| private List<InnerStructA> mListInnerStructA = new ArrayList<InnerStructA>(); |
| |
| @CalledByNative |
| private void addStructA(InnerStructA a) { |
| // Called by the native side to append another element. |
| mListInnerStructA.add(a); |
| } |
| |
| @CalledByNative |
| private void iterateAndDoSomething() { |
| Iterator<InnerStructA> it = mListInnerStructA.iterator(); |
| while (it.hasNext()) { |
| InnerStructA element = it.next(); |
| // Now, do something with element. |
| } |
| // Done, clear the list. |
| mListInnerStructA.clear(); |
| } |
| |
| // This "struct" will be created by the java side passed to native, which |
| // will use its getters. |
| // Note that |@CalledByNative| has to contain the inner class name. |
| static class InnerStructB { |
| private final long mKey; |
| private final String mValue; |
| |
| private InnerStructB(long k, String v) { |
| mKey = k; |
| mValue = v; |
| } |
| |
| @CalledByNative("InnerStructB") |
| private long getKey() { |
| return mKey; |
| } |
| |
| @CalledByNative("InnerStructB") |
| private String getValue() { |
| return mValue; |
| } |
| } |
| |
| List<InnerStructB> mListInnerStructB = new ArrayList<InnerStructB>(); |
| |
| void iterateAndDoSomethingWithMap() { |
| Iterator<InnerStructB> it = mListInnerStructB.iterator(); |
| while (it.hasNext()) { |
| InnerStructB element = it.next(); |
| // Now, do something with element. |
| nativeAddStructB(mNativeCPPObject, element); |
| } |
| nativeIterateAndDoSomethingWithStructB(mNativeCPPObject); |
| } |
| |
| native void nativeAddStructB(long nativeCPPClass, InnerStructB b); |
| native void nativeIterateAndDoSomethingWithStructB(long nativeCPPClass); |
| native String nativeReturnAString(long nativeCPPClass); |
| } |