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