root/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. JNINamespace
  2. startExample
  3. doStuff
  4. finishExample
  5. javaMethod
  6. staticJavaMethod
  7. packagePrivateJavaMethod
  8. methodThatThrowsException
  9. nativeInit
  10. nativeDestroy
  11. nativeGetDoubleFunction
  12. nativeGetFloatFunction
  13. nativeSetNonPODDatatype
  14. nativeGetNonPODDatatype
  15. nativeMethod
  16. NativeClassQualifiedName
  17. nativeMethodOtherP0
  18. CalledByNative
  19. create
  20. addStructA
  21. iterateAndDoSomething
  22. CalledByNative
  23. getKey
  24. CalledByNative
  25. getValue
  26. iterateAndDoSomethingWithMap
  27. nativeAddStructB
  28. nativeIterateAndDoSomethingWithStructB
  29. nativeReturnAString

// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.example.jni_generator;

import android.graphics.Rect;

import org.chromium.base.AccessedByNative;
import org.chromium.base.CalledByNative;
import org.chromium.base.CalledByNativeUnchecked;
import org.chromium.base.JNINamespace;
import org.chromium.base.NativeClassQualifiedName;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

// This class serves as a reference test for the bindings generator, and as example documentation
// for how to use the jni generator.
// The C++ counter-part is sample_for_tests.cc.
// jni_generator.gyp has a jni_generator_tests target that will:
//   * Generate a header file for the JNI bindings based on this file.
//   * Compile sample_for_tests.cc using the generated header file.
//   * link a native executable to prove the generated header + cc file are self-contained.
// All comments are informational only, and are ignored by the jni generator.
//
// Binding C/C++ with Java is not trivial, specially when ownership and object lifetime
// semantics needs to be managed across boundaries.
// Following a few guidelines will make the code simpler and less buggy:
//
// - Never write any JNI "by hand". Rely on the bindings generator to have a thin
// layer of type-safety.
//
// - Treat the types from the other side as "opaque" as possible. Do not inspect any
// object directly, but rather, rely on well-defined getters / setters.
//
// - Minimize the surface API between the two sides, and rather than calling multiple
// functions across boundaries, call only one (and then, internally in the other side,
// call as many little functions as required).
//
// - If a Java object "owns" a native object, stash the pointer in a "long mNativeClassName".
// Note that it needs to have a "destruction path", i.e., it must eventually call a method
// to delete the native object (for example, the java object has a "close()" method that
// in turn deletes the native object). Avoid relying on finalizers: those run in a different
// thread and makes the native lifetime management more difficult.
//
// - For native object "owning" java objects:
//   - If there's a strong 1:1 to relationship between native and java, the best way is to
//   stash the java object into a base::android::ScopedJavaGlobalRef. This will ensure the
//   java object can be GC'd once the native object is destroyed but note that this global strong
//   ref implies a new GC root, so be sure it will not leak and it must never rely on being
//   triggered (transitively) from a java side GC.
//   - In all other cases, the native side should keep a JavaObjectWeakGlobalRef, and check whether
//   that reference is still valid before de-referencing it. Note that you will need another
//   java-side object to be holding a strong reference to this java object while it is in use, to
//   avoid unpredictable GC of the object before native side has finished with it.
//
// - The best way to pass "compound" datatypes across in either direction is to create an inner
// class with PODs and a factory function. If possible, make it immutable (i.e., mark all the
// fields as "final"). See examples with "InnerStructB" below.
//
// - It's simpler to create thin wrappers with a well defined JNI interface than to
// expose a lot of internal details. This is specially significant for system classes where it's
// simpler to wrap factory methods and a few getters / setters than expose the entire class.
//
// - Use static factory functions annotated with @CalledByNative rather than calling the
// constructors directly.
//
// - Iterate over containers where they are originally owned, then create inner structs or
// directly call methods on the other side. It's much simpler than trying to amalgamate
// java and stl containers.
//
// This JNINamespace annotation indicates that all native methods should be
// generated inside this namespace, including the native class that this
// object binds to.
@JNINamespace("base::android")
class SampleForTests {
  // Classes can store their C++ pointer counter part as an int that is normally initialized by
  // calling out a nativeInit() function.
  long mNativeCPPObject;

  // You can define methods and attributes on the java class just like any other.
  // Methods without the @CalledByNative annotation won't be exposed to JNI.
  public SampleForTests() {
  }

  public void startExample() {
      // Calls native code and holds a pointer to the C++ class.
      mNativeCPPObject = nativeInit("myParam");
  }

  public void doStuff() {
      // This will call CPPClass::Method() using nativePtr as a pointer to the object. This must be
      // done to:
      // * avoid leaks.
      // * using finalizers are not allowed to destroy the cpp class.
      nativeMethod(mNativeCPPObject);
  }

  public void finishExample() {
      // We're done, so let's destroy nativePtr object.
      nativeDestroy(mNativeCPPObject);
  }

  // -----------------------------------------------------------------------------------------------
  // The following methods demonstrate exporting Java methods for invocation from C++ code.
  // Java functions are mapping into C global functions by prefixing the method name with
  // "Java_<Class>_"
  // This is triggered by the @CalledByNative annotation; the methods may be named as you wish.

  // Exported to C++ as:
  //   Java_Example_javaMethod(JNIEnv* env, jobject obj, jint foo, jint bar)
  // Typically the C++ code would have obtained the jobject via the Init() call described above.
  @CalledByNative
  public int javaMethod(int foo,
                        int bar) {
      return 0;
  }

  // Exported to C++ as Java_Example_staticJavaMethod(JNIEnv* env)
  // Note no jobject argument, as it is static.
  @CalledByNative
  public static boolean staticJavaMethod() {
      return true;
  }

  // No prefix, so this method is package private. It will still be exported.
  @CalledByNative
  void packagePrivateJavaMethod() {}

  // Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that
  // call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to
  // call ClearException() and act as appropriate.
  // See more details at the "@CalledByNativeUnchecked" annotation.
  @CalledByNativeUnchecked
  void methodThatThrowsException() throws Exception {}

  // The generator is not confused by inline comments:
  // @CalledByNative void thisShouldNotAppearInTheOutput();
  // @CalledByNativeUnchecked public static void neitherShouldThis(int foo);

  /**
   * The generator is not confused by block comments:
   * @CalledByNative void thisShouldNotAppearInTheOutputEither();
   * @CalledByNativeUnchecked public static void andDefinitelyNotThis(int foo);
   */

  // String constants that look like comments don't confuse the generator:
  private String arrgh = "*/*";

  //------------------------------------------------------------------------------------------------
  // Java fields which are accessed from C++ code only must be annotated with @AccessedByNative to
  // prevent them being eliminated when unreferenced code is stripped.
  @AccessedByNative
  private int javaField;

  //------------------------------------------------------------------------------------------------
  // The following methods demonstrate declaring methods to call into C++ from Java.
  // The generator detects the "native" and "static" keywords, the type and name of the first
  // parameter, and the "native" prefix to the function name to determine the C++ function
  // signatures. Besides these constraints the methods can be freely named.

  // This declares a C++ function which the application code must implement:
  //   static jint Init(JNIEnv* env, jobject obj);
  // The jobject parameter refers back to this java side object instance.
  // The implementation must return the pointer to the C++ object cast to jint.
  // The caller of this method should store it, and supply it as a the nativeCPPClass param to
  // subsequent native method calls (see the methods below that take an "int native..." as first
  // param).
  private native long nativeInit(String param);

  // This defines a function binding to the associated C++ class member function. The name is
  // derived from |nativeDestroy| and |nativeCPPClass| to arrive at CPPClass::Destroy() (i.e. native
  // prefixes stripped).
  // The |nativeCPPClass| is automatically cast to type CPPClass* in order to obtain the object on
  // which to invoke the member function.
  private native void nativeDestroy(long nativeCPPClass);

  // This declares a C++ function which the application code must implement:
  //   static jdouble GetDoubleFunction(JNIEnv* env, jobject obj);
  // The jobject parameter refers back to this java side object instance.
  private native double nativeGetDoubleFunction();

  // Similar to nativeGetDoubleFunction(), but here the C++ side will receive a jclass rather than
  // jobject param, as the function is declared static.
  private static native float nativeGetFloatFunction();

  // This function takes a non-POD datatype. We have a list mapping them to their full classpath in
  // jni_generator.py JavaParamToJni. If you require a new datatype, make sure you add to that
  // function.
  private native void nativeSetNonPODDatatype(Rect rect);

  // This declares a C++ function which the application code must implement:
  //   static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, jobject obj);
  // The jobject parameter refers back to this java side object instance.
  // Note that it returns a ScopedJavaLocalRef<jobject> so that you don' have to worry about
  // deleting the JNI local reference.  This is similar with Strings and arrays.
  private native Object nativeGetNonPODDatatype();

  // Similar to nativeDestroy above, this will cast nativeCPPClass into pointer of CPPClass type and
  // call its Method member function.
  private native int nativeMethod(long nativeCPPClass);

  // Similar to nativeMethod above, but here the C++ fully qualified class name is taken from the
  // annotation rather than parameter name, which can thus be chosen freely.
  @NativeClassQualifiedName("CPPClass::InnerClass")
  private native double nativeMethodOtherP0(long nativePtr);

  // This "struct" will be created by the native side using |createInnerStructA|,
  // and used by the java-side somehow.
  // Note that |@CalledByNative| has to contain the inner class name.
  static class InnerStructA {
      private final long mLong;
      private final int mInt;
      private final String mString;

      private InnerStructA(long l, int i, String s) {
          mLong = l;
          mInt = i;
          mString = s;
      }

      @CalledByNative("InnerStructA")
      private static InnerStructA create(long l, int i, String s) {
          return new InnerStructA(l, i, s);
      }
  }

  private List<InnerStructA> mListInnerStructA = new ArrayList<InnerStructA>();

  @CalledByNative
  private void addStructA(InnerStructA a) {
      // Called by the native side to append another element.
      mListInnerStructA.add(a);
  }

  @CalledByNative
  private void iterateAndDoSomething() {
      Iterator<InnerStructA> it = mListInnerStructA.iterator();
      while (it.hasNext()) {
          InnerStructA element = it.next();
          // Now, do something with element.
      }
      // Done, clear the list.
      mListInnerStructA.clear();
  }

  // This "struct" will be created by the java side passed to native, which
  // will use its getters.
  // Note that |@CalledByNative| has to contain the inner class name.
  static class InnerStructB {
      private final long mKey;
      private final String mValue;

      private InnerStructB(long k, String v) {
          mKey = k;
          mValue = v;
      }

      @CalledByNative("InnerStructB")
      private long getKey() {
          return mKey;
      }

      @CalledByNative("InnerStructB")
      private String getValue() {
          return mValue;
      }
  }

  List<InnerStructB> mListInnerStructB = new ArrayList<InnerStructB>();

  void iterateAndDoSomethingWithMap() {
      Iterator<InnerStructB> it = mListInnerStructB.iterator();
      while (it.hasNext()) {
          InnerStructB element = it.next();
          // Now, do something with element.
          nativeAddStructB(mNativeCPPObject, element);
      }
      nativeIterateAndDoSomethingWithStructB(mNativeCPPObject);
  }

  native void nativeAddStructB(long nativeCPPClass, InnerStructB b);
  native void nativeIterateAndDoSomethingWithStructB(long nativeCPPClass);
  native String nativeReturnAString(long nativeCPPClass);
}

/* [<][>][^][v][top][bottom][index][help] */