mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-01 09:46:09 +03:00
119 lines
5.0 KiB
Markdown
119 lines
5.0 KiB
Markdown
|
# Overview
|
||
|
JNI (Java Native Interface) is the mechanism that enables Java code to call
|
||
|
native functions, and native code to call Java functions.
|
||
|
|
||
|
* Native code calls into Java using apis from `<jni.h>`, which basically mirror
|
||
|
Java's reflection APIs.
|
||
|
* Java code calls native functions by declaring body-less functions with the
|
||
|
`native` keyword, and then calling them as normal Java functions.
|
||
|
|
||
|
`jni_generator` generates boiler-plate code with the goal of making our code:
|
||
|
1. easier to write, and
|
||
|
2. typesafe.
|
||
|
|
||
|
`jni_generator` uses regular expressions to parse .Java files, so don't do
|
||
|
anything too fancy. E.g.:
|
||
|
* Classes must be either explicitly imported, or are assumed to be in
|
||
|
the same package. To use `java.lang` classes, add an explicit import.
|
||
|
* Inner classes need to be referenced through the outer class. E.g.:
|
||
|
`void call(Outer.Inner inner)`
|
||
|
|
||
|
The presense of any JNI within a class will result in ProGuard obfuscation for
|
||
|
the class to be disabled.
|
||
|
|
||
|
### Exposing Native Methods
|
||
|
|
||
|
**Without Crazy Linker:**
|
||
|
* Java->Native calls are exported from the shared library and lazily resolved
|
||
|
by the runtime (via `dlsym()`).
|
||
|
|
||
|
**With Crazy Linker:**
|
||
|
* Java->Native calls are explicitly registered with JNI on the native side.
|
||
|
Explicit registration is necessary because crazy linker provides its own
|
||
|
`dlsym()`, but JNI is hardcoded to use the system's `dlsym()`.
|
||
|
* The logic to explicitly register stubs is generated by
|
||
|
`jni_registration_generator.py`.
|
||
|
* This script finds all native methods by scanning all source `.java` files
|
||
|
of an APK. Inefficient, but very convenient.
|
||
|
* Since `dlsym()` is not used in this case, we use a linker script to avoid
|
||
|
the cost of exporting symbols from the shared library (refer to
|
||
|
`//build/config/android:hide_all_but_jni_onload`).
|
||
|
* `jni_registration_generator.py` exposes two registrations methods:
|
||
|
* `RegisterNonMainDexNatives` - Registers native functions needed by multiple
|
||
|
process types (e.g. Rendereres, GPU process).
|
||
|
* `RegisterMainDexNatives` - Registers native functions needed only by the
|
||
|
browser process.
|
||
|
|
||
|
### Exposing Java Methods
|
||
|
|
||
|
Java methods just need to be annotated with `@CalledByNative`. The generated
|
||
|
functions can be put into a namespace using `@JNINamespace("your_namespace")`.
|
||
|
|
||
|
## Usage
|
||
|
|
||
|
Because the generator does not generate any source files, generated headers must
|
||
|
not be `#included` by multiple sources. If there are Java functions that need to
|
||
|
be called by multiple sources, one source should be chosen to expose the
|
||
|
functions to the others via additional wrapper functions.
|
||
|
|
||
|
### Calling Java -> Native
|
||
|
|
||
|
* Methods marked as `native` will have stubs generated for them that forward
|
||
|
calls to C++ function (that you must write).
|
||
|
* If the first parameter is a C++ object (e.g. `long mNativePointer`), then the
|
||
|
bindings will automatically generate the appropriate cast and call into C++
|
||
|
code (JNI itself is only C).
|
||
|
|
||
|
### Calling Native -> Java
|
||
|
|
||
|
* Methods annotated with `@CalledByNative` will have stubs generated for them.
|
||
|
* Just call the generated stubs defined in generated `.h` files.
|
||
|
|
||
|
### Java Objects and Garbage Collection
|
||
|
|
||
|
All pointers to Java objects must be registered with JNI in order to prevent
|
||
|
garbage collection from invalidating them.
|
||
|
|
||
|
For Strings & Arrays - it's common practice to use the `//base/android/jni_*`
|
||
|
helpers to convert them to `std::vectors` and `std::strings` as soon as
|
||
|
possible.
|
||
|
|
||
|
For other objects - use smart pointers to store them:
|
||
|
* `ScopedJavaLocalRef<>` - When lifetime is the current function's scope.
|
||
|
* `ScopedJavaGlobalRef<>` - When lifetime is longer than the current function's
|
||
|
scope.
|
||
|
* `JavaObjectWeakGlobalRef<>` - Weak reference (do not prevent garbage
|
||
|
collection).
|
||
|
* `JavaParamRef<>` - Use to accept any of the above as a parameter to a
|
||
|
function without creating a redundant registration.
|
||
|
|
||
|
### Additional Guidelines / Advice
|
||
|
|
||
|
Minimize the surface API between the two sides. Rather than calling multiple
|
||
|
functions across boundaries, call only one (and then on the other side, call as
|
||
|
many little functions as required).
|
||
|
|
||
|
If a Java object "owns" a native one, store the pointer via
|
||
|
`"long mNativeClassName"`. Ensure to eventually call a native method to delete
|
||
|
the object. For example, have a `close()` that deletes the native object.
|
||
|
|
||
|
The best way to pass "compound" types across in either direction is to
|
||
|
create an inner class with PODs and a factory function. If possible, make mark
|
||
|
all the fields as "final".
|
||
|
|
||
|
## Build Rules
|
||
|
|
||
|
* `generate_jni` - Generates a header file with stubs for given `.java` files
|
||
|
* `generate_jar_jni` - Generates a header file with stubs for a given `.jar`
|
||
|
file
|
||
|
* `generate_jni_registration` - Generates a header file with functions to
|
||
|
register native-side JNI methods (required only when using crazy linker).
|
||
|
|
||
|
Refer to [//build/config/android/rules.gni](https://cs.chromium.org/chromium/src/build/config/android/rules.gni)
|
||
|
for more about the GN templates.
|
||
|
|
||
|
## Changing `jni_generator`
|
||
|
|
||
|
* Python unit tests live in `jni_generator_tests.py`
|
||
|
* A working demo app exists as `//base/android/jni_generator:sample_jni_apk`
|