src/third_party/jni_zero/README.chromium.md
This document describes how to use JNI Zero in Chromium.
[TOC]
Templates are in //third_party/jni_zero/jni_zero.gni.
Given a set of Java files, generates a header file to call into Java for all
@CalledByNative functions. If @NativeMethods is present, also generates a
.srcjar containing <ClassName>Jni.java, which should be depended on via the
generated GN target <generate_jni's target name>_java.
** Example:**
generate_jni("abcd_jni") {
sources = [ "org/chromium/foo/Bar.java" ]
}
android_library("abcd_java") {
...
# For the generated `${Bar}Jni` classes.
deps = [ ":abcd_jni_java" ]
}
source_set("abcd") {
...
# Allows the cc files to include the generated `${OriginalClassName}_jni.h`
# headers.
deps = [ ":abcd_jni" ]
}
Given a .jar file (defaults to android.jar), generates a header file
similar to generate_jni, if every method and public field were annotated by
@CalledByNative.
Generates a whole-program Java and native link - required for all Java that
calls into native via @NativeMethods.
A wrapper around a shared_library that bundles a generate_jni_registration.
Same as shared_library but for a component.
JNI Zero supports automatic type conversions via the @JniType annotation. See
the main README.md for details and a list of conversions
provided by JNI Zero.
Use @JniType("std::string") for UTF-8 or @JniType("std::u16string") for UTF-16.
Java:
void onNameChanged(@JniType("std::string") String name);
@JniType("std::string") String getName();
C++:
#include "base/android/jni_string.h"
#include "path/to/generated_jni/MyClass_jni.h"
void JNI_MyClass_OnNameChanged(JNIEnv* env, const std::string& name) { ... }
std::string JNI_MyClass_GetName(JNIEnv* env) { return "name"; }
Null Handling: null Java strings are converted to empty std::string or std::u16string.
Runnable / Callback / Callback2 to C++ as a base::OnceCallback, use @JniType.Java:
void doSomething(@JniType("base::OnceClosure") Runnable callback);
void fetchSuccess(@JniType("base::OnceCallback<void(std::string)>") Callback<String> callback);
void fetchSuccess2(@JniType("base::OnceCallback<void(const jni_zero::JavaRef<jobject>&, long)>") Callback2<SomeClass, Long> callback);
C++:
#include "base/android/callback_android.h"
#include "path/to/generated_jni/MyClass_jni.h"
void JNI_MyClass_DoSomething(JNIEnv* env, base::OnceClosure callback) {
std::move(callback).Run();
}
void JNI_MyClass_FetchSuccess2(JNIEnv* env, base::OnceCallback<void(const jni_zero::JavaRef<jobject>&, long)> callback) {
std::move(callback).Run(nullptr, 123L);
}
@JniType on the Java side.JniOnceRunnable / JniRepeatingRunnableJniOnceCallback<T> / JniRepeatingCallback<T>,JniOnceCallback2<T1, T2> / JniRepeatingCallback2<T1. T2>,Repeating* variants, you must call destroy() when done with them.Once* variants, destroy() is called when it's run, but you must call it manually if it is never run.Once* variants, can be typed as Runnable / Callback / Callback2 if you don't need destroy().Java:
@CalledByNative
void onResult(@JniType("base::OnceCallback<void(std::string)>&&") JniOnceCallback<String> callback) {
callback.onResult("success");
}
@CalledByNative
void onResult2(@JniType("base::OnceCallback<void(const jni_zero::JavaRef<jobject>&, long)>&&") JniOnceCallback2<SomeClass, Long> callback) {
callback.onResult(new SomeClass(), 123L);
}
@CalledByNative
@JniType("base::OnceClosure")
Runnable getClosure();
C++:
#include "base/android/callback_android.h"
#include "path/to/generated_jni/MyClass_jni.h"
void Finish(JNIEnv* env, base::OnceCallback<void(std::string)> callback) {
Java_MyClass_onResult(env, std::move(callback));
}
void Finish2(JNIEnv* env, base::OnceCallback<void(const jni_zero::JavaRef<jobject>&, long)> callback) {
auto callback = base::BindOnce([](){
...
});
Java_MyClass_onResult2(env, std::move(callback));
}
base::OnceClosure JNI_MyClass_GetClosure(JNIEnv* env) {
return base::BindOnce([](){
...
});
}
Use std::optional<T> to handle nullable parameters.
Java:
void maybeDoSomething(@JniType("std::optional<std::string>") String maybeName);
C++:
#include "third_party/jni_zero/default_conversions.h"
#include "base/android/jni_string.h"
#include "path/to/generated_jni/MyClass_jni.h"
void JNI_MyClass_MaybeDoSomething(JNIEnv* env, std::optional<std::string> maybeName) {
if (maybeName) { ... }
}
Header: #include "third_party/jni_zero/default_conversions.h"
Behavior: null is converted to std::nullopt.
JNI Zero can convert between Java arrays/Lists and C++ containers or base::span.
You can pass a base::span from Native to Java where Java expects an array or a List.
Java:
@CalledByNative
void showItems(@JniType("base::span<const GURL>") GURL[] items);
C++:
#include "url/android/gurl_android.h"
#include "path/to/generated_jni/MyClass_jni.h"
void SendItems(JNIEnv* env, base::span<const GURL> items) {
Java_MyClass_showItems(env, items);
}
Header: #include "third_party/jni_zero/default_conversions.h" (and the
header for the element type, e.g., url/android/gurl_android.h)
Searching for FromJniType or ToJniType specializations will reveal more. Common ones include:
| C++ Type | Java Type | Header |
|---|---|---|
std::string | String | base/android/jni_string.h |
std::u16string | String | base/android/jni_string.h |
base::OnceClosure | Runnable | base/android/callback_android.h |
base::OnceCallback<void(T)> | Callback<T> | base/android/callback_android.h |
base::OnceCallback<void(T1, T2)> | Callback2<T1, T2> | base/android/callback_android.h |
GURL | GURL | url/android/gurl_android.h |
base::Token | Token | base/android/token_android.h |
base::UnguessableToken | UnguessableToken | base/android/unguessable_token_android.h |