1 // Copyright 2012 The Chromium Authors 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.jni_zero.samples; 6 7 import android.graphics.Rect; 8 9 import org.jni_zero.AccessedByNative; 10 import org.jni_zero.CalledByNative; 11 import org.jni_zero.CalledByNativeUnchecked; 12 import org.jni_zero.JNINamespace; 13 import org.jni_zero.NativeClassQualifiedName; 14 import org.jni_zero.NativeMethods; 15 16 import java.util.ArrayList; 17 import java.util.Iterator; 18 import java.util.LinkedList; 19 import java.util.List; 20 import java.util.Map; 21 22 // This class serves as a reference test for the bindings generator, and as example documentation 23 // for how to use the jni generator. 24 // The C++ counter-part is sample_for_tests.cc. 25 // jni_generator/BUILD.gn has a jni_generator_tests target that will: 26 // * Generate a header file for the JNI bindings based on this file. 27 // * Generate a header file containing registration methods required to use C++ methods from this 28 // file. 29 // * Compile sample_for_tests.cc using the generated header file. 30 // * link a native executable to prove the generated header + cc file are self-contained. 31 // All comments are informational only, and are ignored by the jni generator. 32 // 33 // This JNINamespace annotation indicates that all native methods should be 34 // generated inside this namespace, including the native class that this 35 // object binds to. 36 @JNINamespace("base::android") 37 class SampleForTests { 38 // Classes can store their C++ pointer counterpart as an int that is normally initialized by 39 // calling out a SampleForTestsJni.get().init() function. Replace "CPPClass" with your 40 // particular class name! 41 long mNativeCPPObject; 42 43 // You can define methods and attributes on the java class just like any other. 44 // Methods without the @CalledByNative annotation won't be exposed to JNI. SampleForTests()45 public SampleForTests() {} 46 startExample()47 public void startExample() { 48 // Calls C++ Init(...) method and holds a pointer to the C++ class. 49 mNativeCPPObject = SampleForTestsJni.get().init(this, "myParam"); 50 } 51 doStuff()52 public void doStuff() { 53 // This will call CPPClass::Method() using nativePtr as a pointer to the object. This must 54 // be done to: 55 // * avoid leaks. 56 // * using finalizers are not allowed to destroy the cpp class. 57 SampleForTestsJni.get().method(mNativeCPPObject, this); 58 } 59 60 // Just a comment to ensure we aren't reading comments: 61 // private native void thisShouldNotExist(); 62 finishExample()63 public void finishExample() { 64 // We're done, so let's destroy nativePtr object. 65 SampleForTestsJni.get().destroy(mNativeCPPObject, this); 66 } 67 68 // --------------------------------------------------------------------------------------------- 69 // The following methods demonstrate exporting Java methods for invocation from C++ code. 70 // Java functions are mapping into C global functions by prefixing the method name with 71 // "Java_<Class>_" 72 // This is triggered by the @CalledByNative annotation; the methods may be named as you wish. 73 74 // Exported to C++ as: 75 // Java_SampleForTests_javaMethod(JNIEnv* env, jobject caller, jint foo, jint bar) 76 // Typically the C++ code would have obtained the jobject via the Init() call described above. 77 @CalledByNative javaMethod(int foo, int bar)78 public int javaMethod(int foo, int bar) { 79 return 0; 80 } 81 82 // Exported to C++ as Java_SampleForTests_staticJavaMethod(JNIEnv* env) 83 // Note no jobject argument, as it is static. 84 @CalledByNative staticJavaMethod()85 public static boolean staticJavaMethod() { 86 return true; 87 } 88 89 // No prefix, so this method is package private. It will still be exported. 90 @CalledByNative packagePrivateJavaMethod()91 void packagePrivateJavaMethod() {} 92 93 // Method signature with generics in params. 94 @CalledByNative methodWithGenericParams( Map<String, Map<String, String>> foo, LinkedList<Integer> bar)95 public void methodWithGenericParams( 96 Map<String, Map<String, String>> foo, LinkedList<Integer> bar) {} 97 98 // Constructors will be exported to C++ as: 99 // Java_SampleForTests_Constructor(JNIEnv* env, jint foo, jint bar) 100 @CalledByNative SampleForTests(int foo, int bar)101 public SampleForTests(int foo, int bar) {} 102 103 // Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that 104 // call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to 105 // call ClearException() and act as appropriate. 106 // See more details at the "@CalledByNativeUnchecked" annotation. 107 @CalledByNativeUnchecked methodThatThrowsException()108 void methodThatThrowsException() {} 109 110 // The generator is not confused by inline comments: 111 // @CalledByNative void thisShouldNotAppearInTheOutput(); 112 // @CalledByNativeUnchecked public static void neitherShouldThis(int foo); 113 114 /** 115 * The generator is not confused by block comments: 116 * @CalledByNative void thisShouldNotAppearInTheOutputEither(); 117 * @CalledByNativeUnchecked public static void andDefinitelyNotThis(int foo); 118 */ 119 120 // String constants that look like comments don't confuse the generator: 121 private String mArrgh = "*/*"; 122 123 private @interface SomeAnnotation {} 124 private @interface AnotherAnnotation {} 125 126 // The generator is not confused by @Annotated parameters. 127 @CalledByNative javaMethodWithAnnotatedParam(@omeAnnotation int foo, final @SomeAnnotation int bar, @SomeAnnotation final int baz, @SomeAnnotation final @AnotherAnnotation int bat)128 void javaMethodWithAnnotatedParam(@SomeAnnotation int foo, final @SomeAnnotation int bar, 129 @SomeAnnotation final int baz, @SomeAnnotation final @AnotherAnnotation int bat) {} 130 131 // --------------------------------------------------------------------------------------------- 132 // Java fields which are accessed from C++ code only must be annotated with @AccessedByNative to 133 // prevent them being eliminated when unreferenced code is stripped. 134 @AccessedByNative 135 private int mJavaField; 136 137 // This "struct" will be created by the native side using |createInnerStructA|, 138 // and used by the java-side somehow. 139 // Note that |@CalledByNative| has to contain the inner class name. 140 static class InnerStructA { 141 private final long mLong; 142 private final int mInt; 143 private final String mString; 144 InnerStructA(long l, int i, String s)145 private InnerStructA(long l, int i, String s) { 146 mLong = l; 147 mInt = i; 148 mString = s; 149 } 150 151 @CalledByNative("InnerStructA") create(long l, int i, String s)152 private static InnerStructA create(long l, int i, String s) { 153 return new InnerStructA(l, i, s); 154 } 155 } 156 157 private List<InnerStructA> mListInnerStructA = new ArrayList<InnerStructA>(); 158 159 @CalledByNative addStructA(InnerStructA a)160 private void addStructA(InnerStructA a) { 161 // Called by the native side to append another element. 162 mListInnerStructA.add(a); 163 } 164 165 @CalledByNative iterateAndDoSomething()166 private void iterateAndDoSomething() { 167 Iterator<InnerStructA> it = mListInnerStructA.iterator(); 168 while (it.hasNext()) { 169 InnerStructA element = it.next(); 170 // Now, do something with element. 171 } 172 // Done, clear the list. 173 mListInnerStructA.clear(); 174 } 175 176 // This "struct" will be created by the java side passed to native, which 177 // will use its getters. 178 // Note that |@CalledByNative| has to contain the inner class name. 179 static class InnerStructB { 180 private final long mKey; 181 private final String mValue; 182 InnerStructB(long k, String v)183 private InnerStructB(long k, String v) { 184 mKey = k; 185 mValue = v; 186 } 187 188 @CalledByNative("InnerStructB") getKey()189 private long getKey() { 190 return mKey; 191 } 192 193 @CalledByNative("InnerStructB") getValue()194 private String getValue() { 195 return mValue; 196 } 197 } 198 199 List<InnerStructB> mListInnerStructB = new ArrayList<InnerStructB>(); 200 iterateAndDoSomethingWithMap()201 void iterateAndDoSomethingWithMap() { 202 Iterator<InnerStructB> it = mListInnerStructB.iterator(); 203 while (it.hasNext()) { 204 InnerStructB element = it.next(); 205 // Now, do something with element. 206 SampleForTestsJni.get().addStructB(mNativeCPPObject, this, element); 207 } 208 SampleForTestsJni.get().iterateAndDoSomethingWithStructB(mNativeCPPObject, this); 209 } 210 interface InnerInterface {} 211 enum InnerEnum {} 212 213 @CalledByNative getInnerInterface()214 static InnerInterface getInnerInterface() { 215 return null; 216 } 217 218 @CalledByNative getInnerEnum()219 static InnerEnum getInnerEnum() { 220 return null; 221 } 222 223 // Test overloads (causes names to be mangled). 224 @CalledByNative getInnerEnum(int a)225 static InnerEnum getInnerEnum(int a) { 226 return null; 227 } 228 229 // Test jclass and jthrowable, as well as generics. 230 @CalledByNative getClass(Class<Map<String, String>> arg0)231 private Class<Map<String, String>> getClass(Class<Map<String, String>> arg0) { 232 return null; 233 } 234 @CalledByNative getThrowable(Throwable arg0)235 private Throwable getThrowable(Throwable arg0) { 236 return null; 237 } 238 239 // --------------------------------------------------------------------------------------------- 240 // The following methods demonstrate declaring methods to call into C++ from Java. 241 // The generator detects the type and name of the first parameter. 242 @NativeMethods 243 public interface Natives { 244 // This declares a C++ function which the application code must implement: 245 // static jint Init(JNIEnv* env, jobject caller); 246 // The jobject parameter refers back to this java side object instance. 247 // The implementation must return the pointer to the C++ object cast to jint. 248 // The caller of this method should store it, and supply it as a the nativeCPPClass param to 249 // subsequent native method calls (see the methods below that take an "int native..." as 250 // first param). init(SampleForTests caller, String param)251 long init(SampleForTests caller, String param); 252 253 // This defines a function binding to the associated C++ class member function. The name is 254 // derived from |nativeDestroy| and |nativeCPPClass| to arrive at CPPClass::Destroy() (i.e. 255 // native prefixes stripped). 256 // 257 // The |nativeCPPClass| is automatically cast to type CPPClass*, in order to obtain the 258 // object on which to invoke the member function. Replace "CPPClass" with your particular 259 // class name! destroy(long nativeCPPClass, SampleForTests caller)260 void destroy(long nativeCPPClass, SampleForTests caller); 261 262 // This declares a C++ function which the application code must implement: 263 // static jdouble GetDoubleFunction(JNIEnv* env, jobject caller); 264 // The jobject parameter refers back to this java side object instance. getDoubleFunction(SampleForTests caller)265 double getDoubleFunction(SampleForTests caller); 266 267 // Similar to nativeGetDoubleFunction(), but here the C++ side will receive a jclass rather 268 // than jobject param, as the function is declared static. getFloatFunction()269 float getFloatFunction(); 270 271 // This function takes a non-POD datatype. We have a list mapping them to their full 272 // classpath in jni_generator.py JavaParamToJni. If you require a new datatype, make sure 273 // you add to that function. setNonPODDatatype(SampleForTests caller, Rect rect)274 void setNonPODDatatype(SampleForTests caller, Rect rect); 275 276 // This declares a C++ function which the application code must implement: 277 // static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, jobject caller); 278 // The jobject parameter refers back to this java side object instance. 279 // Note that it returns a ScopedJavaLocalRef<jobject> so that you don' have to worry about 280 // deleting the JNI local reference. This is similar with Strings and arrays. getNonPODDatatype(SampleForTests caller)281 Object getNonPODDatatype(SampleForTests caller); 282 283 // Test jclass and jthrowable, as well as generics. getClass(Class<Map<String, String>> arg0)284 Class<Map<String, String>> getClass(Class<Map<String, String>> arg0); getThrowable(Throwable arg0)285 Throwable getThrowable(Throwable arg0); 286 287 // Similar to nativeDestroy above, this will cast nativeCPPClass into pointer of CPPClass 288 // type and call its Method member function. Replace "CPPClass" with your particular class 289 // name! method(long nativeCPPClass, SampleForTests caller)290 int method(long nativeCPPClass, SampleForTests caller); 291 292 // Similar to nativeMethod above, but here the C++ fully qualified class name is taken from 293 // the annotation rather than parameter name, which can thus be chosen freely. 294 @NativeClassQualifiedName("CPPClass::InnerClass") methodOtherP0(long nativePtr, SampleForTests caller)295 double methodOtherP0(long nativePtr, SampleForTests caller); 296 297 // Tests passing a nested class. addStructB(long nativeCPPClass, SampleForTests caller, InnerStructB b)298 void addStructB(long nativeCPPClass, SampleForTests caller, InnerStructB b); 299 iterateAndDoSomethingWithStructB(long nativeCPPClass, SampleForTests caller)300 void iterateAndDoSomethingWithStructB(long nativeCPPClass, SampleForTests caller); returnAString(long nativeCPPClass, SampleForTests caller)301 String returnAString(long nativeCPPClass, SampleForTests caller); 302 } 303 } 304