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