Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Interacting with Java Classes and Methods using C++ JNI

Tech 1

The Java Native Interface (JNI) provides a bridge for C++ code to interact with Java components in Android development. This process involves locating the class definition, retrieving method identifiers, instantiating objects if necessary, and finally executing the target logic.

Java Side Definition

Consider a Java class that contains both an instance-level action and a static utility function:

package com.example.utils;

public class LoggerService {
    public void logAction(String message) {
        System.out.println("Log: " + message);
    }

    public static int computeHash(int input) {
        return input ^ 0x5F3759DF;
    }
}

Implementation in C++

To interact with the Java layer, the C++ code must use the JNIEnv pointer to navigate the Java Virtual Machine (JVM).

1. Locating the Java Class

The FindClass function uses the fully qualified name (with slashes instead of dots) to locate the class metadata.

jclass serviceClass = env->FindClass("com/example/utils/LoggerService");
if (serviceClass == nullptr) {
    return JNI_ERR;
}

2. Reoslving Method Identifiers

Method IDs are handles used to call specific functions. JNI distinguishes between instance methods and static methods. The method signature must match the Java descriptor exactly.

// Instance method signature: (Ljava/lang/String;)V (takes a String, returns void)
jmethodID logId = env->GetMethodID(serviceClass, "logAction", "(Ljava/lang/String;)V");

// Static method signature: (I)I (takes an int, returns an int)
jmethodID hashId = env->GetStaticMethodID(serviceClass, "computeHash", "(I)I");

3. Object Instantiation

There are two primary ways to create a Java object from C++:

  • NewObject: Allocates memory and invokes a constructor immediately.
  • AllocObject: Allocates memory without running a constructor (initialization must be handled manually later if needed).
// Retrieve the default constructor ID
jmethodID constructor = env->GetMethodID(serviceClass, "<init>", "()V");

// Option 1: Instantiate via constructor
jobject serviceObj = env->NewObject(serviceClass, constructor);

// Option 2: Allocate memory only
// jobject serviceObj = env->AllocObject(serviceClass);

4. Executing Methods and Exception Handling

When calling methods, ensure that native C++ types are converted to JNI types (e.g., std::string to jstring).

// Invoking the instance method
jstring logMsg = env->NewStringUTF("Native call initiated");
env->CallVoidMethod(serviceObj, logId, logMsg);

// Checking for Java exceptions after the call
if (env->ExceptionCheck()) {
    env->ExceptionDescribe();
    env->ExceptionClear();
}

// Invoking the static method directly via the class reference
jint result = env->CallStaticIntMethod(serviceClass, hashId, 1024);

Integrated Native Function Example

The following logic demonstrates a complete cycle of class resolution and method invocation within a native JNI function.

extern "C" JNIEXPORT jint JNICALL
Java_com_example_app_MainActivity_executeNativeLogic(JNIEnv *env, jobject thiz) {
    // 1. Resolve Class
    jclass targetClass = env->FindClass("com/example/utils/LoggerService");
    if (!targetClass) return -1;

    // 2. Resolve Method IDs
    jmethodID midLog = env->GetMethodID(targetClass, "logAction", "(Ljava/lang/String;)V");
    jmethodID midHash = env->GetStaticMethodID(targetClass, "computeHash", "(I)I");

    // 3. Instantiate
    jmethodID init = env->GetMethodID(targetClass, "<init>", "()V");
    jobject instance = env->NewObject(targetClass, init);

    // 4. Perform Invocations
    jstring msg = env->NewStringUTF("Operation Successful");
    env->CallVoidMethod(instance, midLog, msg);
    
    if (env->ExceptionCheck()) {
        env->ExceptionClear();
    }

    jint hashedValue = env->CallStaticIntMethod(targetClass, midHash, 500);
    
    return hashedValue;
}
Tags: AndroidJNI

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.