Commit 97395ba7 authored by agrieve's avatar agrieve Committed by Commit Bot

Update JNI Generator docs to mention JCaller.

Bug: 898261
Change-Id: Idc5d040a535be7b03b326ed30a902166b63d9b48
Reviewed-on: https://chromium-review.googlesource.com/c/1374138
Commit-Queue: agrieve <agrieve@chromium.org>
Reviewed-by: default avatarEric Stevenson <estevenson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#616009}
parent 27dfaff2
...@@ -60,7 +60,7 @@ functions to the others via additional wrapper functions. ...@@ -60,7 +60,7 @@ functions to the others via additional wrapper functions.
There are two ways to call native methods: There are two ways to call native methods:
**Method 1 - Using the 'native' keyword (legacy for static methods)** **Method 1 - Using the 'native' keyword (soon to be deprecated)**
- Works for both static and non-static methods. - Works for both static and non-static methods.
- Methods marked as `native` will have stubs generated for them that forward - Methods marked as `native` will have stubs generated for them that forward
...@@ -71,83 +71,70 @@ There are two ways to call native methods: ...@@ -71,83 +71,70 @@ There are two ways to call native methods:
**Method 2 - Using an interface annotated with @NativeMethods** **Method 2 - Using an interface annotated with @NativeMethods**
- Only works for static methods. - Declare methods using a nested interface annotated with `@NativeMethods`.
- Supports mocking in tests. - The JNI annotation processor generates a class named `${OriginalClassName}Jni`
- Inner-interfaces annotated with `@NativeMethods` defines the native with a `get()` method that returns an implementation of the annotated
interface. interface. The C++ function that it routes to is the same as if it would be
- A companion JNI class is generated which implements this interface with the in the legacy method.
proper bindings to forward calls to C++ functions (that you must write).
- See example below for usage. - See example below for usage.
#### Mocking Native Methods To add JNI to a class:
- Native functions cannot be called in JUnit tests and so classes that contain 1. Enable the JNI processor by adding to your `android_library` target:
native functions are difficult to unit test. ```python
- Using the JNI annotation processor, wrapper classes are generated at compile annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
time to support swapping between mock native implementations for tests. ```
- Currently only static methods can be mocked in this way without any overhead 2. Create a nested-interface annotated with `@NativeMethods` that contains
(All classes added are removed after being optimized by R8). See
[this bug](https://crbug.com/898261) for details.
Usage:
1. Enable the JNI processor by adding
`annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]`
to the Java target.
2. Create an inner-interface annotated with `@NativeMethods` that contains
the declaration of the corresponding static methods you wish to have the declaration of the corresponding static methods you wish to have
implemented. implemented.
3. Call native functions using `${OriginalClassName}Jni.get().${method}` e.g. 3. Call native functions using `${OriginalClassName}Jni.get().${method}`
Example:
```java ```java
// The following classes would have the same generated native bindings. // The following classes would have the same generated native bindings (with the
// exception of differing class names).
// Legacy/deprecated static methods // Legacy/deprecated static methods
class A { class Legacy {
static native void nativeFoo(); static native void nativeFoo();
static native double nativeBar(int a, int b); static native double nativeBar(int a, int b);
native void nativeNonStatic(long nativePointer);
void callNatives() { void callNatives() {
nativeFoo() nativeFoo()
nativeBar(1,2); nativeBar(1,2);
nativeNonStatic(mNativePointer);
} }
} }
// Equivalent using an interface: // Equivalent using new style:
class A { class NewStyle {
void callNatives() {
// AJni is a generated by the JNI annotation processor.
// AJni.get() returns an instance of A.Natives.
// For testing AJni.TEST_HOOKS allows tests to setup mocks.
AJni.get().foo();
AJni.get().bar(1,2);
// All natives must be called through AJni.get().
// Storing AJni.get() in a field breaks optimizations made by R8.
// Using AJni.get() everywhere has no overhead when running in release with R8.
}
@NativeMethods @NativeMethods
interface Natives { interface Natives {
void foo(); void foo();
double bar(int a, int b); double bar(int a, int b);
// @JCaller is passed to C++ as the java "this" object, and nativePointer
// as the C++ "this" object.
void nonStatic(@JCaller NewStyle self, long nativePointer);
}
void callNatives() {
// NewStyleJni is generated by the JNI annotation processor.
// Storing NewStyleJni.get() in a field defeats some of the desired R8
// optimizations, but local variables are fine.
Natives jni = NewStyleJni.get();
jni.foo();
jni.bar(1,2);
jni.nonStatic(this, mNativePointer);
} }
} }
``` ```
- If a class contains an interface annotated with `@NativeMethods`, the JNI
annotation processor generates a class named `${OriginalClassName}Jni` which
has a function `get()` which returns an implementation of the annotated
interface (and will call the corresponding static method as if it
were declared in the top level class with `static native` keywords)
Optimization of these generated classes is delicate. Storing the natives
interface returned by `get()` in a field **breaks the optimization**, so calling
`get()` is necessary each time a native function is invoked.
#### Testing Mockable Natives #### Testing Mockable Natives
1. Add the `JniMocker` rule to your test. 1. Add the `JniMocker` rule to your test.
2. Call `JniMocker#mock` in a `setUp()` method for each interface you want to stub 2. Call `JniMocker#mock` in a `setUp()` method for each interface you want to
out. (Using TEST_HOOKS) stub out.
JniMocker will reset the stubs during `tearDown()`. JniMocker will reset the stubs during `tearDown()`.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment