• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
SampleForTests()101     public SampleForTests() {
102     }
103 
startExample()104     public void startExample() {
105         // Calls C++ Init(...) method and holds a pointer to the C++ class.
106         mNativeCPPObject = nativeInit("myParam");
107     }
108 
doStuff()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 
finishExample()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
javaMethod(int foo, int bar)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
staticJavaMethod()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
packagePrivateJavaMethod()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
methodThatThrowsException()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
javaMethodWithAnnotatedParam(@omeAnnotation int foo)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).
nativeInit(String 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!
nativeDestroy(long nativeCPPClass)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.
nativeGetDoubleFunction()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.
nativeGetFloatFunction()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.
nativeSetNonPODDatatype(Rect rect)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.
nativeGetNonPODDatatype()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!
nativeMethod(long nativeCPPClass)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")
nativeMethodOtherP0(long nativePtr)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 
InnerStructA(long l, int i, String s)243         private InnerStructA(long l, int i, String s) {
244             mLong = l;
245             mInt = i;
246             mString = s;
247         }
248 
249         @CalledByNative("InnerStructA")
create(long l, int i, String s)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
addStructA(InnerStructA a)258     private void addStructA(InnerStructA a) {
259         // Called by the native side to append another element.
260         mListInnerStructA.add(a);
261     }
262 
263     @CalledByNative
iterateAndDoSomething()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 
InnerStructB(long k, String v)281         private InnerStructB(long k, String v) {
282             mKey = k;
283             mValue = v;
284         }
285 
286         @CalledByNative("InnerStructB")
getKey()287         private long getKey() {
288             return mKey;
289         }
290 
291         @CalledByNative("InnerStructB")
getValue()292         private String getValue() {
293             return mValue;
294         }
295     }
296 
297     List<InnerStructB> mListInnerStructB = new ArrayList<InnerStructB>();
298 
iterateAndDoSomethingWithMap()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 
nativeAddStructB(long nativeCPPClass, InnerStructB b)309     native void nativeAddStructB(long nativeCPPClass, InnerStructB b);
nativeIterateAndDoSomethingWithStructB(long nativeCPPClass)310     native void nativeIterateAndDoSomethingWithStructB(long nativeCPPClass);
nativeReturnAString(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")
nativeGetInnerIntFunction()316         private static native int nativeGetInnerIntFunction();
317     }
318 }
319