Android JNI (Java Native Interface) technology (used in conjunction with NDK (Native Development Kit)) Java interacts with C++, and Java calls the C++ interface

What is the interaction between Java and C++ (or other languages)? Under what circumstances is jni needed?

“Interaction between Java and C++” refers to the fact that the two languages can call each other’s methods and operate each other’s data. For example, you can call C++ functions in Java code, and you can also call Java methods in C++ code; you can operate objects created by C++ in Java code, and you can also operate Java objects in C++ code. The object created.

If you just call a C++ function in Java code, and all parameters and return values of this function are basic types (such as int, float, etc.), then it can indeed be simply regarded as “calling a C++ function” “. But once more complex situations are involved, such as passing strings, arrays, or custom objects, more complex “interactions” are required. For example, you might need to create a Java object in C++ code, or modify the contents of a Java array in C++ code.

JNI provides a set of rules and interfaces to make this “interaction” possible. For example, it defines how Java strings are converted to C++ strings, how Java arrays are converted to C++ arrays, and how Java methods are called in C++ code, etc.

In general, if there is a part of the function in your Android application that needs to be implemented in C++, and this part of the function needs to interact with Java code (that is, not just simply calling a C++ function), then you need to use JNI.

Article directory

  • Android JNI (Java Native Interface) technology
    • 1. What is JNI?
    • 2. Why is JNI important?
    • 3. How to use JNI in Android projects?
      • 1. Create native methods: Declare one or more native methods in a Java class.
      • 2. Generate JNI header file: Use the `javah` command to generate a C/C++ header file for your Java class.
      • 3. Implement native methods: Implement these native methods in C/C++ code.
      • 4. Compile and package: Use NDK (Native Development Kit) to compile your C/C++ code and package it into a .so library.
      • 5. Load and use the native library: Load this .so library in Java code and call the native method in it.
    • 4. Analysis of difficult technical points
      • 4.1 String operations
      • 4.2 Exception handling
    • 5. Command and code demonstration
    • 6. JNI method naming convention (Java_package name_class name_method name)
      • About java static methods and non-static methods
    • Summarize

Android JNI (Java Native Interface) technology

1. What is JNI?

JNI (Java Native Interface) is part of the Java platform, which allows Java code to interact with code written in other languages. This means that we can call functions written in C, C++ or assembly language etc. from a Java environment and vice versa.

2. Why is JNI important?

For Android developers, JNI has the following three main advantages:

  • Performance: For certain types of applications, especially those that require a lot of mathematical calculations or graphics processing, using C/C++ will usually have better performance than Java.
  • Reuse existing code: If you have some libraries written in C or C++, then through JNI, you can use these libraries directly in Android applications without rewriting the code.
  • Access low-level API: In some cases, the Android SDK does not provide APIs to access hardware or operating system-level functions. At this point, we can use JNI to implement these functions.

3. How to use JNI in Android projects?

To use JNI in an Android project, you need to follow the following steps:

1. Create native methods: Declare one or more native methods in the Java class.

2. Generate JNI header file: Use the javah command to generate a C/C++ header file for your Java class.

3. Implement native methods: Implement these native methods in C/C++ code.

4. Compilation and packaging: Use NDK (Native Development Kit) to compile your C/C++ code and package it into a .so library.

5. Load and use native library: Load this .so library in Java code and call the native method in it.

4. Analysis of difficult technical points

4.1 String operations

String operations in JNI can cause some confusion because Java strings and C/C++ strings are different. Java uses UTF-16 encoding, while C/C++ usually uses UTF-8 or ASCII encoding. Therefore, when passing strings between Java and C/C++, we need to convert character encodings.

JNIEXPORT void JNICALL Java_com_example_MyClass_myMethod(JNIEnv *env, jobject obj, jstring str) {<!-- -->
    const char *cStr = (*env)->GetStringUTFChars(env, str, 0);
    //Use cStr...
    (*env)->ReleaseStringUTFChars(env, str, cStr);
}

The code above demonstrates how to get a C-style string of a Java string in JNI and ensure it is properly deallocated after use.

4.2 Exception handling

In JNI code, if Java methods throw exceptions, we need to check and handle these exceptions correctly. Otherwise, if other JNI functions are called when no caught exception exists, the program will crash.

JNIEXPORT void JNICALL Java_com_example_MyClass_myMethod(JNIEnv *env, jobject obj) {<!-- -->
    jclass cls = (*env)->FindClass(env, "com/example/MyException");
    if (cls == NULL) {<!-- --> return; } // Class not found exception
    (*env)->ThrowNew(env, cls, "exception message");
}

The above code demonstrates how to throw a Java exception in JNI code. We first use the FindClass function to get the exception class, and then use the ThrowNew function to throw the exception.

5. Command and code demonstration

Next, let’s take a look at how to use JNI in an actual Android project. Suppose we have a Java class that contains a native method:

public class MyClass {<!-- -->
    public native void myMethod(String str);
}

We can use the javah command to generate a JNI header file for this class:

javah -jni com.example.MyClass

Then, we can implement this native method in C/C++ code:

#include "com_example_MyClass.h"

JNIEXPORT void JNICALL Java_com_example_MyClass_myMethod(JNIEnv *env, jobject obj, jstring str) {<!-- -->
    const char *cStr = (*env)->GetStringUTFChars(env, str, 0);
    //Use cStr...
    (*env)->ReleaseStringUTFChars(env, str, cStr);
}

Finally, we can use the NDK to compile this C/C++ code and package it into a .so library:

ndk-build NDK_PROJECT_PATH=.APP_BUILD_SCRIPT=./Android.mk

Then load this .so library in Java code and call the native method:

static {<!-- -->
    System.loadLibrary("mylibrary");
}

public void callMyMethod() {<!-- -->
    MyClass obj = new MyClass();
    obj.myMethod("Hello, JNI!");
}

6. JNI method naming convention (Java_package name_class name_method name)

The naming rules for JNI methods are a specific convention defined by the JNI specification. When you declare a native method in a Java class, you need to provide an implementation of the method in native code. In order to correctly associate a native method in Java with its C/C++ implementation, C/C++ functions must be named according to the specific format specified by JNI.

The following is the general format of the JNI method naming rules:

Java_package name_class name_method name

Among them, the description of each part is as follows:

  • Java_: All JNI function names start with “Java_”.
  • Package name: This part is the package name of the Java class, in which each period (.’) is replaced with an underscore (_’).
  • Class name: This part is the name of the Java class that contains the native method.
  • Method name: This part is the name of the native method.

Therefore, for a myMethod method declared in the com.example.MyClass class, its JNI function name should be Java_com_example_MyClass_myMethod.

Note that if the native method is a non-static method, then the second parameter type of the JNI function is jobject; if the native method is static, then the second parameter type is jclass code>.

About java static methods and non-static methods

In Java, methods (also called functions) can be declared static or non-static. There are several key differences between these two types of methods:

Static Methods

  • Static methods are declared using the static keyword. For example: public static void myMethod() {...}.
  • Static methods belong to the class, not to instances of the class. Therefore, static methods can be called without creating an instance of the class. You can call them directly through the class name, for example: MyClass.myMethod().
  • Static methods cannot directly access non-static fields (variables) and non-static methods of the class, because these fields and methods need to be accessed on the object of the class.

Non-static Methods or Instance Methods

  • Non-static methods do not use the static keyword. For example: public void myMethod() {...}.
  • Non-static methods belong to instances (objects) of a class, not the class. Therefore, an instance of a class must first be created before calling non-static methods of the class, for example: MyClass obj = new MyClass(); obj.myMethod();.
  • Non-static methods can directly access all fields (variables) and methods of the class, both static and non-static.

In the JNI environment, if the native method is static, then the second parameter type of the JNI function is jclass; if the native method is non-static, then the second parameter type is jobject. This reflects the difference between static and non-static methods in Java: static methods belong to classes, while non-static methods belong to instances of classes.

Summary

Overall, JNI is a powerful tool that allows us to build bridges between Java code and other languages. However, there are also some issues that need to be paid attention to when using JNI, such as string operations, exception handling, etc.