• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Overview
2JNI (Java Native Interface) is the mechanism that enables Java code to call
3native functions, and native code to call Java functions.
4
5 * Native code calls into Java using apis from `<jni.h>`, which basically mirror
6   Java's reflection APIs.
7 * Java code calls native functions by declaring body-less functions with the
8  `native` keyword, and then calling them as normal Java functions.
9
10`jni_generator` generates boiler-plate code with the goal of making our code:
11 1. easier to write, and
12 2. typesafe.
13
14`jni_generator` uses regular expressions to parse .Java files, so don't do
15anything too fancy. E.g.:
16 * Classes must be either explicitly imported, or are assumed to be in
17the same package. To use `java.lang` classes, add an explicit import.
18 * Inner classes need to be referenced through the outer class. E.g.:
19   `void call(Outer.Inner inner)`
20
21The presense of any JNI within a class will result in ProGuard obfuscation for
22the class to be disabled.
23
24### Exposing Native Methods
25
26There are two ways to have native methods be found by Java:
271) Explicitly register the name -> function pointer mapping using JNI's
28   `RegisterNatives()` function.
292) Export the symbols from the shared library, and let the runtime resolve them
30   on-demand (using `dlsym()`) the first time a native method is called.
31
322) Is generally preferred due to a smaller code size and less up-front work, but
331) is sometimes required (e.g. when OS bugs prevent `dlsym()` from working).
34Both ways are supported by this tool.
35
36### Exposing Java Methods
37
38Java methods just need to be annotated with `@CalledByNative`. The generated
39functions can be put into a namespace using `@JNINamespace("your_namespace")`.
40
41## Usage
42
43Because the generator does not generate any source files, generated headers must
44not be `#included` by multiple sources. If there are Java functions that need to
45be called by multiple sources, one source should be chosen to expose the
46functions to the others via additional wrapper functions.
47
48### Calling Java -> Native
49
50- Declare methods using a nested interface annotated with `@NativeMethods`.
51- The `generate_jni` rule generates a class named `${OriginalClassName}Jni`
52  with a `get()` method that returns an implementation of the annotated
53  interface. The C++ function that it routes to is the same as if it would be
54  in the legacy method.
55- For each JNI method:
56  - C++ stubs are generated that forward to C++ functions that you must write.
57  - If the first parameter is a C++ object (e.g.
58    `long native${OriginalClassName}`), then the bindings will generate the
59    appropriate cast and call into C++ code.
60
61To add JNI to a class:
62
631. Find or add a `generate_jni` target with your .java file, then add this
64   `generate_jni` target to your `srcjar_deps` of your `android_library` target:
65   ```python
66   generate_jni("abcd_jni")
67   ...
68   android_library("abcd_java") {
69   ...
70     srcjar_deps = [ ":abcd_jni" ]
71   ```
722. Create a nested-interface annotated with `@NativeMethods` that contains
73   the declaration of the corresponding static methods you wish to have
74   implemented.
753. Call native functions using `${OriginalClassName}Jni.get().${method}`
764. In C++ code, #include the header `${OriginalClassName}_jni.h`. (The path will
77   depend on the location of the `generate_jni` BUILD rule that lists your Java
78   source code.) Only include this header from a single `.cc` file as the
79   header defines functions. That `.cc` must implement your native code by
80   defining non-member functions named `JNI_${OriginalClassName}_${UpperCamelCaseMethod}`
81   for static methods and member functions named `${OriginalClassName}::${UpperCamelCaseMethod}`
82   for non-static methods. Member functions need be declared in the header
83   file as well.
84
85Example:
86#### Java
87```java
88class MyClass {
89  // Cannot be private. Must be package or public.
90  @NativeMethods
91  /* package */ interface Natives {
92    void foo();
93    double bar(int a, int b);
94    // Either the |MyClass| part of the |nativeMyClass| parameter name must
95    // match the native class name exactly, or the method annotation
96    // @NativeClassQualifiedName("MyClass") must be used.
97    //
98    // If the native class is nested, use
99    // @NativeClassQualifiedName("FooClassName::BarClassName") and call the
100    // parameter |nativePointer|.
101    void nonStatic(long nativeMyClass);
102  }
103
104  void callNatives() {
105    // MyClassJni is generated by the generate_jni rule.
106    // Storing MyClassJni.get() in a field defeats some of the desired R8
107    // optimizations, but local variables are fine.
108    Natives jni = MyClassJni.get();
109    jni.foo();
110    jni.bar(1,2);
111    jni.nonStatic(mNativePointer);
112  }
113}
114```
115#### C++
116```c++
117#include "base/android/jni_android.h"
118#include "<path to BUILD.gn>/<generate_jni target name>/MyClass_jni.h"
119
120class MyClass {
121public:
122  void NonStatic(JNIEnv* env);
123}
124
125// Notice that unlike Java, function names are capitalized in C++.
126// Static function names should follow this format and don't need to be declared.
127void JNI_MyClass_Foo(JNIEnv* env) { ... }
128void JNI_MyClass_Bar(JNIEnv* env, jint a, jint b) { ... }
129
130// Member functions need to be declared.
131void MyClass::NonStatic(JNIEnv* env) { ... }
132```
133
134**Using the 'native' keyword**
135
136- The binding generator also looks for `native` JNI method declarations and
137  generates stubs for them. This used to be the norm, but is now obsolete.
138
139#### Testing Mockable Natives
140
1411. Add the `JniMocker` rule to your test.
1422. Call `JniMocker#mock` in a `setUp()` method for each interface you want to
143   stub out.
144
145`JniMocker` will reset the stubs during `tearDown()`.
146
147```java
148/**
149 * Tests for {@link AnimationFrameTimeHistogram}
150 */
151@RunWith(BaseRobolectricTestRunner.class)
152@Config(manifest = Config.NONE)
153public class AnimationFrameTimeHistogramTest {
154    @Rule
155    public JniMocker mocker = new JniMocker();
156
157    @Mock
158    AnimationFrameTimeHistogram.Natives mNativeMock;
159
160    @Before
161    public void setUp() {
162        MockitoAnnotations.initMocks(this);
163        mocker.mock(AnimationFrameTimeHistogramJni.TEST_HOOKS, mNativeMock);
164    }
165
166    @Test
167    public void testNatives() {
168        AnimationFrameTimeHistogram hist = new AnimationFrameTimeHistogram("histName");
169        hist.startRecording();
170        hist.endRecording();
171        verify(mNativeMock).saveHistogram(eq("histName"), any(long[].class), anyInt());
172    }
173}
174```
175
176If a native method is called without setting a mock in a unit test, an
177`UnsupportedOperationException` will be thrown.
178
179#### Special case: DFMs
180DFMs have their own generated `GEN_JNI`s, which are `<module_name>_GEN_JNI`. In
181order to get your DFM's JNI to use the `<module_name>` prefix, you must add your
182module name into the argument of the `@NativeMethods` annotation.
183
184So, for example, say your module was named `test_module`. You would annotate
185your `Natives` interface with `@NativeMethods("test_module")`, and this would
186result in `test_module_GEN_JNI`.
187
188
189### Testing for readiness: use `get()`
190
191JNI Generator automatically produces asserts that verify that the Natives interface can be safely
192called. These checks are compiled out of Release builds, making these an excellent way to determine
193whether your code is called safely.
194
195It is not sufficient, however, to use `<Class>Jni.get()` to guarantee native is initialized - it is
196only a debugging tool to ensure that you're using native after native is loaded.
197
198If you expect your code to be called by an external caller, it's often helpful to know _ahead of
199time_ that the context is valid (ie. either native libraries are loaded or mocks are installed).
200In this case it is helpful to call `get()` method, that performs all the Debug checks listed
201above, but does not instantiate a new object for interfacing Native libraries.
202Note that the unused value returned by the `get()` method will be optimized away in release builds
203so there's no harm in ignoring it.
204
205#### Addressing `Jni.get()` exceptions.
206
207When you identify a scenario leading to an exception, relocate (or defer) the appropriate call to
208be made to a place where (or time when) you know the native libraries have been initialized (eg.
209`onStartWithNative`, `onNativeInitialized` etc).
210
211Please avoid calling `LibraryLoader.isInitialized()` / `LibraryLoader.isLoaded()` in new code.
212Using `LibraryLoader` calls makes unit-testing more difficult:
213* this call can not verify whether Mock object is used, making the use of mocks more complicated,
214* using `LibraryLoader.setLibrariesLoadedForNativeTests()` alters the state for subsequently
215executed tests, inaccurately reporting flakiness and failures of these victim tests.
216* Introducing `LibraryLoader.is*()` calls in your code immediately affects all callers, forcing
217the authors of the code up the call stack to override `LibraryLoader` internal state in order to be
218able to unit-test their code.
219
220However, if your code is going to be called both before and after native is initialized, you are
221forced to call `LibraryLoader.isInitialized()` to be able to differentiate. Calling
222`<Class>Jni.get()` only provides assertions, and will fail in debug builds if you call it when
223native isn't ready.
224
225### Calling Native -> Java
226
227 * Methods annotated with `@CalledByNative` will have stubs generated for them.
228   * Inner class methods must provide the inner class name explicitly
229     (ex. `@CalledByNative("InnerClassName")`)
230 * Just call the generated stubs defined in generated `.h` files.
231 * For test-only methods you want to call from native, use
232   `@CalledByNativeForTesting` which will ensure that it is stripped in our
233   release binaries.
234
235### Java Objects and Garbage Collection
236
237All pointers to Java objects must be registered with JNI in order to prevent
238garbage collection from invalidating them.
239
240For Strings & Arrays - it's common practice to use the `//base/android/jni_*`
241helpers to convert them to `std::vectors` and `std::strings` as soon as
242possible.
243
244For other objects - use smart pointers to store them:
245 * `ScopedJavaLocalRef<>` - When lifetime is the current function's scope.
246 * `ScopedJavaGlobalRef<>` - When lifetime is longer than the current function's
247   scope.
248 * `JavaObjectWeakGlobalRef<>` - Weak reference (do not prevent garbage
249   collection).
250 * `JavaParamRef<>` - Use to accept any of the above as a parameter to a
251   function without creating a redundant registration.
252
253### Additional Guidelines / Advice
254
255Minimize the surface API between the two sides. Rather than calling multiple
256functions across boundaries, call only one (and then on the other side, call as
257many little functions as required).
258
259If a Java object "owns" a native one, store the pointer via
260`"long mNativeClassName"`. Ensure to eventually call a native method to delete
261the object. For example, have a `close()` that deletes the native object.
262
263The best way to pass "compound" types across in either direction is to
264create an inner class with PODs and a factory function. If possible, mark
265all the fields as "final".
266
267## Build Rules
268
269 * `generate_jni` - Generates a header file with stubs for given `.java` files
270 * `generate_jar_jni` - Generates a header file with stubs for a given `.jar`
271   file
272 * `generate_jni_registration` - Generates a header file with functions to
273   register native-side JNI methods.
274
275Refer to [//build/config/android/rules.gni](https://cs.chromium.org/chromium/src/build/config/android/rules.gni)
276for more about the GN templates.
277
278## Changing `jni_generator`
279
280 * Python tests live in `integration_tests.py`
281 * A working demo app exists as `//third_party/jni_zero/samples:sample_jni_apk`
282