What is JNI?
JNI is a standardized API that provides:- Two-way communication: Java/Kotlin can call C/C++ functions, and C/C++ can call back into Java/Kotlin
- Type system mapping: Conversions between Java types and C types
- Object access: Native code can create, inspect, and modify Java objects
- Exception handling: Propagate and handle exceptions across the language boundary
- Memory management: Coordinate between garbage-collected Java heap and manually-managed native memory
JNI is a standard Java feature, not Android-specific. However, Android makes heavy use of JNI for its native components.
How JNI works
Basic architecture
The JNI layer handles:- Function dispatch: Routing Java native method calls to C/C++ implementations
- Type marshaling: Converting data between Java and C representations
- Reference management: Tracking object references across the boundary
- Thread synchronization: Ensuring thread-safe access to JVM state
Native method declaration
In Java/Kotlin, declare methods with thenative keyword:
- Kotlin
- Java
Native implementation
C/C++ implementations follow a naming convention:C++ can use a cleaner syntax with the
-> operator instead of (*env)->. C++ also allows automatic JNI method registration.JNI types and signatures
Type mappings
Java types map to corresponding C types:Method signatures
Method signatures encode parameter and return types:JNIEnv interface
TheJNIEnv* pointer provides access to all JNI functions:
Common operations
String operations
String operations
Array operations
Array operations
Object operations
Object operations
Exception handling
Exception handling
Reference management
JNI has three types of object references:Local references
Automatically managed within native method scope:Global references
Persist across native method calls:Weak global references
Like global references but don’t prevent garbage collection:Thread considerations
JNIEnv is thread-local
Each thread has its ownJNIEnv*:
Attaching native threads
Native threads must attach to access JNI:Performance considerations
JNI call overhead
Crossing the JNI boundary has cost:- Bad: Many small calls
- Good: Bulk operations
Direct buffers
For large data transfer, use direct ByteBuffers:Critical sections
For short, performance-critical array access:Best practices
Check for errors
Always check for NULL and exceptions:Use helper macros
Simplify error checking:Cache class and method IDs
Lookups are expensive, cache them:Next steps
Now that you understand JNI:- Learn about ABIs for multi-architecture builds
- Understand bionic C library specifics
- Explore build systems for compiling native code
For complete JNI reference, see Oracle’s JNI Specification.