ANDROID JNIENV
Understanding JNIEnv in Android JNI
When working with JNI (Java Native Interface) in Android, one of the most important objects that native code interacts with is the JNIEnv. This object provides the link between Java and native (C/C++) code, allowing native methods to access and manipulate Java objects, call Java methods, and manage memory for Java objects.
In this article, we’ll explore what JNIEnv is, how it works, and how you can use it effectively within your Android application when interacting with native code.
What is JNIEnv?
In JNI (Java Native Interface), the JNIEnv object is a pointer to a structure that provides an interface for native code to interact with the Java Virtual Machine (JVM). It allows native methods to access Java objects, invoke Java methods, and access various Java classes and their members (fields, methods, etc.).
Every native method in JNI receives a pointer to JNIEnv as one of its arguments, and it is used throughout the method to perform operations on Java objects.
JNIEnv Structure
JNIEnvis not an actual object but a pointer to a structure that encapsulates a variety of methods and functionality for interacting with the JVM.- It contains methods for calling Java methods, creating Java objects, working with arrays, strings, and more.
Key Roles of JNIEnv
The JNIEnv pointer is crucial for various JNI operations. Some of its main functions include:
-
Accessing Java Classes:
JNIEnvhelps the native code to find and work with Java classes using methods likeFindClass(). -
Creating and Managing Java Objects: Native code can create new Java objects and interact with them using
JNIEnv. For example, it can useNewObject()to create a new instance of a Java class. -
Calling Java Methods:
JNIEnvprovides the ability to call Java methods from C or C++ code. You can use functions likeCallVoidMethod()to call Java methods with specific signatures. -
Handling Java Strings: Working with strings is a common task in JNI.
JNIEnvallows native code to convert C-style strings to JavaStringobjects usingNewStringUTF()and to convert Java strings back to C-style strings. -
Accessing Java Fields: Native code can read and write Java object fields using methods such as
GetFieldID()andSetObjectField().
How Does JNIEnv Work?
In a typical JNI setup, the native method gets a pointer to the JNIEnv structure when it is called by Java. This pointer is passed as the first argument to every JNI function.
Let’s break down an example of how JNIEnv is used in a native method to interact with Java code.
Example of Using JNIEnv in JNI
Let's create a simple example where native code calls a Java method using JNIEnv.
Step 1: Java Code
In the MainActivity.java, we define a native method and a Java method that will be invoked by the native code:
package com.example.jniexample;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
// Declare the native method
public native void nativeMethod();
// This method will be called from native code
public void onMessageReceived(String message) {
TextView textView = findViewById(R.id.textView);
textView.setText(message); // Display the message from native code
}
static {
// Load the native library
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Call the native method
nativeMethod();
}
}
In this code:
nativeMethod()is the method that will be implemented in C/C++ using JNI.onMessageReceived()will be the Java method that is called from the native code.
Step 2: Native Code in C++
Now, let's implement the nativeMethod() in the native C++ code (native-lib.cpp). This native method will use the JNIEnv pointer to call the onMessageReceived() method in Java.
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jniexample_MainActivity_nativeMethod(JNIEnv *env, jobject thiz) {
// The native code generates a message
std::string message = "Hello from C++";
// Get the class of the Java object (MainActivity)
jclass mainActivityClass = env->GetObjectClass(thiz);
// Get the method ID for the Java method we want to call
jmethodID methodID = env->GetMethodID(mainActivityClass, "onMessageReceived", "(Ljava/lang/String;)V");
if (methodID == nullptr) {
return; // Handle error if the method is not found
}
// Convert the C++ string to a Java string
jstring javaMessage = env->NewStringUTF(message.c_str());
// Call the Java method (onMessageReceived) from native code
env->CallVoidMethod(thiz, methodID, javaMessage);
// Clean up the local references
env->DeleteLocalRef(javaMessage);
env->DeleteLocalRef(mainActivityClass);
}
Explanation of the Native Code:
-
Accessing the Java Class:
env->GetObjectClass(thiz)is used to get thejclass(Java class) of the object that called the native method (thizrefers to the JavaMainActivityobject).
-
Getting the Method ID:
env->GetMethodID()retrieves the method ID for theonMessageReceivedmethod inMainActivity. This method takes the method name and its signature as arguments.
-
Calling the Java Method:
env->CallVoidMethod()is used to call theonMessageReceivedmethod in Java. It passes the Java object (thiz), the method ID, and the arguments to the method (javaMessage).
-
Creating Java Strings:
env->NewStringUTF()converts the C++std::stringto a JavaString. This is necessary because JNI requires Java objects to be passed as Java types, not native types.
Step 3: Building and Running the Application
After implementing the Java and native code, make sure the project is configured correctly to use JNI. Ensure the following:
- The native library (
native-lib) is loaded in Java usingSystem.loadLibrary("native-lib");. - The CMake configuration file (
CMakeLists.txt) is properly set up to include the C++ source files.
Finally, when you run the app, the native method nativeMethod() will be invoked, and the message "Hello from C++" will be passed back to Java through the callback method onMessageReceived(). This will update the TextView in the UI with the message.
Summary
To summarize, JNIEnv is a critical object in the JNI framework, allowing native C/C++ code to interact with the Java Virtual Machine. Using JNIEnv, native code can:
- Call Java methods.
- Access and modify Java fields.
- Create and manipulate Java objects.
- Work with Java data types such as strings, arrays, and classes.
In this tutorial, we've demonstrated how to use JNIEnv to implement JNI callbacks, allowing native code to invoke Java methods and pass data back to the Java side. By leveraging JNIEnv, you can effectively bridge Java and native code to build powerful and performance-optimized Android applications.

0 Comments