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.
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.
- Methods marked as `native` will have stubs generated for them that forward
......@@ -71,83 +71,70 @@ There are two ways to call native methods:
**Method 2 - Using an interface annotated with @NativeMethods**
- Only works for static methods.
- Supports mocking in tests.
- Inner-interfaces annotated with `@NativeMethods` defines the native
interface.
- A companion JNI class is generated which implements this interface with the
proper bindings to forward calls to C++ functions (that you must write).
- Declare methods using a nested interface annotated with `@NativeMethods`.
- The JNI annotation processor generates a class named `${OriginalClassName}Jni`
with a `get()` method that returns an implementation of the annotated
interface. The C++ function that it routes to is the same as if it would be
in the legacy method.
- 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
native functions are difficult to unit test.
- Using the JNI annotation processor, wrapper classes are generated at compile
time to support swapping between mock native implementations for tests.
- Currently only static methods can be mocked in this way without any overhead
(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
1. Enable the JNI processor by adding to your `android_library` target:
```python
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
```
2. Create a nested-interface annotated with `@NativeMethods` that contains
the declaration of the corresponding static methods you wish to have
implemented.
3. Call native functions using `${OriginalClassName}Jni.get().${method}` e.g.
3. Call native functions using `${OriginalClassName}Jni.get().${method}`
Example:
```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
class A {
class Legacy {
static native void nativeFoo();
static native double nativeBar(int a, int b);
native void nativeNonStatic(long nativePointer);
void callNatives() {
nativeFoo()
nativeBar(1,2);
nativeNonStatic(mNativePointer);
}
}
// Equivalent using an interface:
class A {
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.
}
// Equivalent using new style:
class NewStyle {
@NativeMethods
interface Natives {
void foo();
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
1. Add the `JniMocker` rule to your test.
2. Call `JniMocker#mock` in a `setUp()` method for each interface you want to stub
out. (Using TEST_HOOKS)
2. Call `JniMocker#mock` in a `setUp()` method for each interface you want to
stub out.
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