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