Home | History | Annotate | Download | only in jni_generator
      1 // Copyright 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.example.jni_generator;
      6 
      7 import android.graphics.Rect;
      8 
      9 import org.chromium.base.annotations.AccessedByNative;
     10 import org.chromium.base.annotations.CalledByNative;
     11 import org.chromium.base.annotations.CalledByNativeUnchecked;
     12 import org.chromium.base.annotations.JNINamespace;
     13 import org.chromium.base.annotations.NativeCall;
     14 import org.chromium.base.annotations.NativeClassQualifiedName;
     15 
     16 import java.util.ArrayList;
     17 import java.util.Iterator;
     18 import java.util.List;
     19 
     20 // This class serves as a reference test for the bindings generator, and as example documentation
     21 // for how to use the jni generator.
     22 // The C++ counter-part is sample_for_tests.cc.
     23 // jni_generator/BUILD.gn has a jni_generator_tests target that will:
     24 //   * Generate a header file for the JNI bindings based on this file.
     25 //   * Compile sample_for_tests.cc using the generated header file.
     26 //   * link a native executable to prove the generated header + cc file are self-contained.
     27 // All comments are informational only, and are ignored by the jni generator.
     28 //
     29 // Binding C/C++ with Java is not trivial, specially when ownership and object lifetime
     30 // semantics needs to be managed across boundaries.
     31 // Following a few guidelines will make the code simpler and less buggy:
     32 //
     33 // - Never write any JNI "by hand". Rely on the bindings generator to have a thin
     34 // layer of type-safety.
     35 //
     36 // - Treat the types from the other side as "opaque" as possible. Do not inspect any
     37 // object directly, but rather, rely on well-defined getters / setters.
     38 //
     39 // - Minimize the surface API between the two sides, and rather than calling multiple
     40 // functions across boundaries, call only one (and then, internally in the other side,
     41 // call as many little functions as required).
     42 //
     43 // - If a Java object "owns" a native object, stash the pointer in a "long mNativeClassName".
     44 // Note that it needs to have a "destruction path", i.e., it must eventually call a method
     45 // to delete the native object (for example, the java object has a "close()" method that
     46 // in turn deletes the native object). Avoid relying on finalizers: those run in a different
     47 // thread and makes the native lifetime management more difficult.
     48 //
     49 // - For native object "owning" java objects:
     50 //   - If there's a strong 1:1 to relationship between native and java, the best way is to
     51 //   stash the java object into a base::android::ScopedJavaGlobalRef. This will ensure the
     52 //   java object can be GC'd once the native object is destroyed but note that this global strong
     53 //   ref implies a new GC root, so be sure it will not leak and it must never rely on being
     54 //   triggered (transitively) from a java side GC.
     55 //   - In all other cases, the native side should keep a JavaObjectWeakGlobalRef, and check whether
     56 //   that reference is still valid before de-referencing it. Note that you will need another
     57 //   java-side object to be holding a strong reference to this java object while it is in use, to
     58 //   avoid unpredictable GC of the object before native side has finished with it.
     59 //
     60 // - The best way to pass "compound" datatypes across in either direction is to create an inner
     61 // class with PODs and a factory function. If possible, make it immutable (i.e., mark all the
     62 // fields as "final"). See examples with "InnerStructB" below.
     63 //
     64 // - It's simpler to create thin wrappers with a well defined JNI interface than to
     65 // expose a lot of internal details. This is specially significant for system classes where it's
     66 // simpler to wrap factory methods and a few getters / setters than expose the entire class.
     67 //
     68 // - Use static factory functions annotated with @CalledByNative rather than calling the
     69 // constructors directly.
     70 //
     71 // - Iterate over containers where they are originally owned, then create inner structs or
     72 // directly call methods on the other side. It's much simpler than trying to amalgamate
     73 // java and stl containers.
     74 //
     75 // An important note about qualified class name resolution:
     76 // The generator doesn't compile the class and have little context about the
     77 // classes being passed through the JNI layers. It adds a few simple rules:
     78 //
     79 // - all classes are either explicitly imported, or they are assumed to be in
     80 // the same package.
     81 //
     82 // - Inner class needs to be done through an import and usage of the
     83 // outer class, so that the generator knows how to qualify it:
     84 // import foo.bar.Zoo;
     85 // void call(Zoo.Inner);
     86 //
     87 // - implicitly imported classes aren't supported, so in order to pass
     88 // things like Runnable, please import java.lang.Runnable;
     89 //
     90 // This JNINamespace annotation indicates that all native methods should be
     91 // generated inside this namespace, including the native class that this
     92 // object binds to.
     93 @JNINamespace("base::android")
     94 class SampleForTests {
     95     // Classes can store their C++ pointer counter part as an int that is normally initialized by
     96     // calling out a nativeInit() function. Replace "CPPClass" with your particular class name!
     97     long mNativeCPPObject;
     98 
     99     // You can define methods and attributes on the java class just like any other.
    100     // Methods without the @CalledByNative annotation won't be exposed to JNI.
    101     public SampleForTests() {
    102     }
    103 
    104     public void startExample() {
    105         // Calls C++ Init(...) method and holds a pointer to the C++ class.
    106         mNativeCPPObject = nativeInit("myParam");
    107     }
    108 
    109     public void doStuff() {
    110         // This will call CPPClass::Method() using nativePtr as a pointer to the object. This must
    111         // be done to:
    112         // * avoid leaks.
    113         // * using finalizers are not allowed to destroy the cpp class.
    114         nativeMethod(mNativeCPPObject);
    115     }
    116 
    117     public void finishExample() {
    118         // We're done, so let's destroy nativePtr object.
    119         nativeDestroy(mNativeCPPObject);
    120     }
    121 
    122     // ---------------------------------------------------------------------------------------------
    123     // The following methods demonstrate exporting Java methods for invocation from C++ code.
    124     // Java functions are mapping into C global functions by prefixing the method name with
    125     // "Java_<Class>_"
    126     // This is triggered by the @CalledByNative annotation; the methods may be named as you wish.
    127 
    128     // Exported to C++ as:
    129     // Java_SampleForTests_javaMethod(JNIEnv* env, jobject caller, jint foo, jint bar)
    130     // Typically the C++ code would have obtained the jobject via the Init() call described above.
    131     @CalledByNative
    132     public int javaMethod(int foo, int bar) {
    133         return 0;
    134     }
    135 
    136     // Exported to C++ as Java_SampleForTests_staticJavaMethod(JNIEnv* env)
    137     // Note no jobject argument, as it is static.
    138     @CalledByNative
    139     public static boolean staticJavaMethod() {
    140         return true;
    141     }
    142 
    143     // No prefix, so this method is package private. It will still be exported.
    144     @CalledByNative
    145     void packagePrivateJavaMethod() {
    146     }
    147 
    148     // Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that
    149     // call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to
    150     // call ClearException() and act as appropriate.
    151     // See more details at the "@CalledByNativeUnchecked" annotation.
    152     @CalledByNativeUnchecked
    153     void methodThatThrowsException() throws Exception {}
    154 
    155     // The generator is not confused by inline comments:
    156     // @CalledByNative void thisShouldNotAppearInTheOutput();
    157     // @CalledByNativeUnchecked public static void neitherShouldThis(int foo);
    158 
    159     /**
    160      * The generator is not confused by block comments:
    161      * @CalledByNative void thisShouldNotAppearInTheOutputEither();
    162      * @CalledByNativeUnchecked public static void andDefinitelyNotThis(int foo);
    163      */
    164 
    165     // String constants that look like comments don't confuse the generator:
    166     private String mArrgh = "*/*";
    167 
    168     private @interface SomeAnnotation {}
    169 
    170     // The generator is not confused by @Annotated parameters.
    171     @CalledByNative
    172     void javaMethodWithAnnotatedParam(@SomeAnnotation int foo) {
    173     }
    174 
    175     // ---------------------------------------------------------------------------------------------
    176     // Java fields which are accessed from C++ code only must be annotated with @AccessedByNative to
    177     // prevent them being eliminated when unreferenced code is stripped.
    178     @AccessedByNative
    179     private int mJavaField;
    180 
    181     // ---------------------------------------------------------------------------------------------
    182     // The following methods demonstrate declaring methods to call into C++ from Java.
    183     // The generator detects the "native" and "static" keywords, the type and name of the first
    184     // parameter, and the "native" prefix to the function name to determine the C++ function
    185     // signatures. Besides these constraints the methods can be freely named.
    186 
    187     // This declares a C++ function which the application code must implement:
    188     // static jint Init(JNIEnv* env, jobject caller);
    189     // The jobject parameter refers back to this java side object instance.
    190     // The implementation must return the pointer to the C++ object cast to jint.
    191     // The caller of this method should store it, and supply it as a the nativeCPPClass param to
    192     // subsequent native method calls (see the methods below that take an "int native..." as first
    193     // param).
    194     private native long nativeInit(String param);
    195 
    196     // This defines a function binding to the associated C++ class member function. The name is
    197     // derived from |nativeDestroy| and |nativeCPPClass| to arrive at CPPClass::Destroy() (i.e.
    198     // native prefixes stripped).
    199     //
    200     // The |nativeCPPClass| is automatically cast to type CPPClass*, in order to obtain the object
    201     // on
    202     // which to invoke the member function. Replace "CPPClass" with your particular class name!
    203     private native void nativeDestroy(long nativeCPPClass);
    204 
    205     // This declares a C++ function which the application code must implement:
    206     // static jdouble GetDoubleFunction(JNIEnv* env, jobject caller);
    207     // The jobject parameter refers back to this java side object instance.
    208     private native double nativeGetDoubleFunction();
    209 
    210     // Similar to nativeGetDoubleFunction(), but here the C++ side will receive a jclass rather than
    211     // jobject param, as the function is declared static.
    212     private static native float nativeGetFloatFunction();
    213 
    214     // This function takes a non-POD datatype. We have a list mapping them to their full classpath
    215     // in jni_generator.py JavaParamToJni. If you require a new datatype, make sure you add to that
    216     // function.
    217     private native void nativeSetNonPODDatatype(Rect rect);
    218 
    219     // This declares a C++ function which the application code must implement:
    220     // static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, jobject caller);
    221     // The jobject parameter refers back to this java side object instance.
    222     // Note that it returns a ScopedJavaLocalRef<jobject> so that you don' have to worry about
    223     // deleting the JNI local reference. This is similar with Strings and arrays.
    224     private native Object nativeGetNonPODDatatype();
    225 
    226     // Similar to nativeDestroy above, this will cast nativeCPPClass into pointer of CPPClass type
    227     // and call its Method member function. Replace "CPPClass" with your particular class name!
    228     private native int nativeMethod(long nativeCPPClass);
    229 
    230     // Similar to nativeMethod above, but here the C++ fully qualified class name is taken from the
    231     // annotation rather than parameter name, which can thus be chosen freely.
    232     @NativeClassQualifiedName("CPPClass::InnerClass")
    233     private native double nativeMethodOtherP0(long nativePtr);
    234 
    235     // This "struct" will be created by the native side using |createInnerStructA|,
    236     // and used by the java-side somehow.
    237     // Note that |@CalledByNative| has to contain the inner class name.
    238     static class InnerStructA {
    239         private final long mLong;
    240         private final int mInt;
    241         private final String mString;
    242 
    243         private InnerStructA(long l, int i, String s) {
    244             mLong = l;
    245             mInt = i;
    246             mString = s;
    247         }
    248 
    249         @CalledByNative("InnerStructA")
    250         private static InnerStructA create(long l, int i, String s) {
    251             return new InnerStructA(l, i, s);
    252         }
    253     }
    254 
    255     private List<InnerStructA> mListInnerStructA = new ArrayList<InnerStructA>();
    256 
    257     @CalledByNative
    258     private void addStructA(InnerStructA a) {
    259         // Called by the native side to append another element.
    260         mListInnerStructA.add(a);
    261     }
    262 
    263     @CalledByNative
    264     private void iterateAndDoSomething() {
    265         Iterator<InnerStructA> it = mListInnerStructA.iterator();
    266         while (it.hasNext()) {
    267             InnerStructA element = it.next();
    268             // Now, do something with element.
    269         }
    270         // Done, clear the list.
    271         mListInnerStructA.clear();
    272     }
    273 
    274     // This "struct" will be created by the java side passed to native, which
    275     // will use its getters.
    276     // Note that |@CalledByNative| has to contain the inner class name.
    277     static class InnerStructB {
    278         private final long mKey;
    279         private final String mValue;
    280 
    281         private InnerStructB(long k, String v) {
    282             mKey = k;
    283             mValue = v;
    284         }
    285 
    286         @CalledByNative("InnerStructB")
    287         private long getKey() {
    288             return mKey;
    289         }
    290 
    291         @CalledByNative("InnerStructB")
    292         private String getValue() {
    293             return mValue;
    294         }
    295     }
    296 
    297     List<InnerStructB> mListInnerStructB = new ArrayList<InnerStructB>();
    298 
    299     void iterateAndDoSomethingWithMap() {
    300         Iterator<InnerStructB> it = mListInnerStructB.iterator();
    301         while (it.hasNext()) {
    302             InnerStructB element = it.next();
    303             // Now, do something with element.
    304             nativeAddStructB(mNativeCPPObject, element);
    305         }
    306         nativeIterateAndDoSomethingWithStructB(mNativeCPPObject);
    307     }
    308 
    309     native void nativeAddStructB(long nativeCPPClass, InnerStructB b);
    310     native void nativeIterateAndDoSomethingWithStructB(long nativeCPPClass);
    311     native String nativeReturnAString(long nativeCPPClass);
    312 
    313     // This inner class shows how to annotate native methods on inner classes.
    314     static class InnerClass {
    315         @NativeCall("InnerClass")
    316         private static native int nativeGetInnerIntFunction();
    317     }
    318 }
    319