This document provides foundational guidance for using JNI Bind, a header-only C++20 metaprogramming library that simplifies JNI interactions.
Important
The Golden Rule: Do not use raw ("porcelain") JNI (e.g.,
env->CallIntMethod, jobject handles, manual DeleteLocalRef) unless
absolutely necessary. JNI Bind provides type-safe, RAII-compliant wrappers
for all standard JNI operations.
The library requires a jni::JvmRef to manage the JavaVM and thread
attachment.
#include "third_party/jni_wrapper/jni_bind.h"
// This object must outlive all JNI Bind calls.
static std::unique_ptr<jni::JvmRef<jni::kDefaultJvm>> jvm;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pjvm, void* reserved) {
jvm = std::make_unique<jni::JvmRef<jni::kDefaultJvm>>(pjvm);
return JNI_VERSION_1_6;
}Define Java classes as static constexpr jni::Class.
static constexpr jni::Class kMyClass{
"com/example/MyClass",
jni::Constructor{jint{}},
jni::Method{"foo", jni::Return<jint>{}, jni::Params<jfloat>{}},
jni::Field{"bar", jint{}}
};Use jni::Overload within jni::Method to handle multiple signatures.
jni::Method{"foo",
jni::Overload{jni::Return<int>{}, jni::Params<>{}},
jni::Overload{jni::Return<int>{}, jni::Params<jfloat>{}}
}For methods that return this, use jni::Self{} to maintain full class
decoration.
jni::Method{"setVal", jni::Return{jni::Self{}}, jni::Params<jint>{}}jni::LocalObject: RAII wrapper for localjobjectreferences.jni::GlobalObject: RAII wrapper for globaljobjectreferences (thread-safe).
// Construction from Java jobject
jni::LocalObject<kMyClass> obj{java_jobject};
// Manual construction from C++
jni::LocalObject<kMyClass> new_obj{123};
// Returning to Java (Releases RAII ownership)
return obj.Release();jint result = obj.Call<"foo">(1.5f);obj.Access<"bar">().Set(42);
jint val = obj.Access<"bar">().Get();Use jni::LocalString or jni::GlobalString. Use PinAsStr() for raw access.
jni::LocalString my_str{"Hello world"};
std::string cpp_str = my_str.PinAsStr().ToString();Use jni::LocalArray. Use Pin() for high-performance bulk access.
jni::LocalArray<jint> arr{10};
{
auto view = arr.Pin();
view.ptr()[0] = 100;
}Access static methods/fields via jni::StaticRef.
static constexpr jni::Class kHelper{
"com/example/Helper",
jni::Static{
jni::Method{"staticFoo", jni::Return<void>{}}
}
};
jni::StaticRef<kHelper>{}.Call<"staticFoo">();Use jni::LocalException to catch, build, and throw Java exceptions.
static constexpr jni::Class kCustomException{"com/example/MyException"};
void ThrowSomething() {
jni::LocalException<kCustomException>{"Failed!"}.Throw();
}Advanced loading from non-primordial loaders.
static constexpr jni::ClassLoader kLoader{
jni::kDefaultClassLoader, jni::SupportedClassSet{kMyClass}};
jni::LocalClassLoader<kLoader> loader{java_loader_obj};
auto my_obj = loader.BuildLocalObject<kMyClass>();Use jni::ThreadGuard in every new native thread.
std::thread([]() {
jni::ThreadGuard guard;
jni::StaticRef<kMyClass>{}.Call<"foo">();
}).detach();blaze test //third_party/jni_wrapper/...To update jni_bind_release.h:
blaze build //third_party/jni_wrapper:gen_jni_bind_releasecp ../../blaze-genfiles/third_party/jni_wrapper/jni_bind_release.h jni_bind_release.h