Understanding JNI and Creating Your First Native Library in Android
The Java Native Interface (JNI) enables Java applications to interact with native code written in languages like C or C++. This mechanism allows Java programs to invoke functions in native libraries and vice versa.
Core Concepts
-
Purpose of JNI:
- Integrating existing C/C++ libraries to reuse efficient code.
- Enhancing performance for computationally intensive tasks.
- Accessing platform-specific features not available through Java.
-
JNI Environment:
- Java Virtual Machine (JVM): JNI operates within the JVM, using its interfaces to communicate with native code.
- Native Library: Contains implementation in native languages, typically as dynamic link libraries (e.g., .dll on Windows or .so on Unix-like systems).
-
Structure Overview:
- Declaring Native Methods: In Java, a method is declared with the
nativekeyword. - Loading Libraries: Use
System.loadLibrary()to load the native library containing the implementation. - Implementing Native Functions: Implement matching methods in C/C++, utilizing JNI APIs for data exchange.
- Declaring Native Methods: In Java, a method is declared with the
Step-by-Step Guide to Using JNI in Android
1. Setting Up a New Project in Android Studio
In modern versions of Android Studio, when creating a new project, select the Native C++ template. After naming your project and choosing between Java and Kotlin, proceed to configure the C++ standard. Select C++11 and finalize the setup. This creates a foundational environment for JNI development.
2. Project Structure Overview
The project includes a cpp directory for storing C++ files and a CMakeLists.txt file defining how to compile and link native components.
Below is the default content of CMakeLists.txt:
cmake_minimum_required(VERSION 3.22.1)
project("firstjni")
add_library(
firstjni
SHARED
native-lib.cpp
)
find_library(
log-lib
log
)
target_link_libraries(
firstjni
${log-lib}
)
Key functionalities of this configuration:
- Defines the minimal required CMake version.
- Names the project
firstjni. - Builds a shared library named
firstjni, usingnative-lib.cpp. - Locates the
loglibrary provided by the Android NDK. - Links the
loglibrary to thefirstjnilibrary to enable logging capabilities.
3. JNI Function Signature and Implementation
Upon creating a new activity, Android Studio generates boilerplate code including a native library loader:
static {
System.loadLibrary("firstjni");
}
It also defines a native method in Java:
public native String stringFromJNI();
When navigating to the C++ implementation via Android Studio’s icon, you’ll find the corresponding function:
extern "C" JNIEXPORT jstring JNICALL
Java_com_marxist_firstjni_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
This is a standard JNI function returning a Java string from C++.
Function Breakdown:
extern "C": Ensures C-style linkage, avoiding C++ name mangling.JNIEXPORT: Makes the function accessible to the JVM.jstring: Return type repreesnting a Java string.JNICALL: Ensures correct calling convention for JNI.Java_com_marxist_firstjni_MainActivity_stringFromJNI: Fully qualified name derived from package and class name, with dots replaced by underscores.JNIEnv* env: Pointer to JNI environment providing access to JVM functions.jobject: Reference to the Java object invoking the method.
4. Running the Example
To display the result in an Android UI component, call the native method from Java:
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
After building and running the application, the output will show the message "Hello from C++".