name: jni-type-conversion description: How to use @JniType annotations for ergonomic JNI. Relevant for Java files that use @NativeMethods or @CalledByNative.
JNI Type Conversion
This skill guides the process of replacing explicit JNI conversion logic (like
ConvertJavaStringToUTF8) with @JniType annotations in Java and corresponding
native types in C++.
Workflow
- Read
third_party/jni_zero/README.chromium.mdfor a primer on JNI Zero. - Identify Candidates: Look for JNI methods (annotated with
@NativeMethodsor@CalledByNative) that take or return types that are currently being explicitly converted in C++. - Discovery (CRITICAL): To see if a type already has a
@JniTypeconversion defined, search the codebase forFromJniTypeorToJniTypedefinitions for that C++ type:
If a conversion exists, note the header file where it is defined; you will need to include it from any C++ files that require the conversion.rg -g "*.h" "\binline .*(From|To)JniType" - Check C++ Implementation: Verify that the C++ side performs explicit
conversions using functions like:
ConvertJavaStringToUTF8->std::stringConvertJavaStringToUTF16->std::u16stringJavaIntArrayToIntVector->std::vector<int32_t>ToJavaArrayOfStrings->std::vector<std::string>base::android::ConvertJavaStringToUTF8->std::string
- Verify Constraints: Do NOT convert if:
- The conversion is conditional (e.g., inside an
ifblock that might skip it). - The conversion happens inside a lambda (e.g.,
TRACE_EVENTmacros). Moving these to@JniTypemakes the conversion eager, which can impact performance.
- The conversion is conditional (e.g., inside an
- Annotate Java:
- Add
@JniType("cpp_type")to the parameter or return type. - For
Stringparameters,@JniType("std::string")automatically converts Javanullto C++"". Prefer this overstd::optional<std::string>unless the C++ logic specifically distinguishes betweennulland empty. - For all other types, use
std::optional<T>if the Java parameter is@Nullable. Always maintain@Nullableannotations. - Binary Data: Use
@JniType("std::vector<uint8_t>")forbyte[]. - Null Safety: Keep
@Nullablein Java if the parameter can be null. For@Nullable String, usingstd::optional<std::string>in C++ will mapnulltostd::nullopt. - Ensure
org.jni_zero.JniTypeis imported.
- Add
- Update C++:
- Change the C++ parameter type to the native type (e.g.,
const std::string&,std::vector<int32_t>&,base::OnceClosure). - Remove the explicit conversion calls and intermediate variables.
- Remove Unused JNIEnv: If the
JNIEnv* envparameter used to be used, but is no longer used after @JniType additions, it should be removed from the C++ function signature. - Remove Unused Callers: For non-static
@NativeMethods, thecallerparameter is usually unnecessary. Remove it from Java and C++ to reduce boilerplate. - Remove Unused using statements: Aliases of conversion functions might no longer have any uses. e.g.: "using base::android::ConvertJavaStringToUTF8"
- Include Order: Specialization headers MUST be included before the
generated
_jni.hfile. - Include the header file that defines the FromJniType / ToJniType conversion
functions.
- E.g.: Include
base/android/jni_string.hfor all string conversions. - E.g.: Include
third_party/jni_zero/default_conversions.hfor containers (std::vector,std::optional,base::span). - E.g.: Include
base/android/callback_android.hfor callback conversions.
- E.g.: Include
- Change the C++ parameter type to the native type (e.g.,
- Validate (CRITICAL): Changes are INCOMPLETE until you have verified they
build. Build all .cc and .java files to ensure JNI generation and compilation
succeed.
- Build using a command like:
autoninja -C OUTPUT_DIR ../../path/to/foo.cc^ ../../path/to/Foo.java^ ...- Paths must be relative to
OUTPUT_DIR(e.g. start with../../) - The "^" suffix means "build all targets that have this input.
- Paths must be relative to
- Do not guess the
OUTPUT_DIRyou must have been told it. - If you cannot build, you MUST state this clearly and summarize the changes made.
- Build using a command like:
Common Recipes
base::Uuid Handling
Java: @JniType("std::string") String uuid C++:
base::Uuid::ParseLowercase(uuid_string) (incoming) or
uuid.AsLowercaseString() (outgoing).
Collection Return Types
@JniType("std::vector<...>") works for return types. C++ can return a
std::vector and it will be automatically converted to a Java array or List.
Examples
Callback Parameter
Java:
void doSomething(@JniType("base::OnceClosure") Runnable callback);
C++:
#include "base/android/callback_android.h"
void JNI_MyClass_DoSomething(base::OnceClosure callback) {
std::move(callback).Run();
}