[Android -- JNI and NDK] Know JNI

foreword

  • In the Android ecosystem, there are mainly three languages: C/C++, Java, and Kotlin. Their relationship is not replacement but complementarity. Among them, the context of C/C++ is algorithm and high performance, the context of Java is platform-independent and memory management, and Kotlin combines the excellent features of multiple languages, bringing a more modern programming method;

  • JNI is a feature that realizes the interaction between Java code and C/C++ code. Think about a question-how does the Java virtual machine realize the interaction between two irrelevant languages? Today, we will comprehensively summarize the JNI development knowledge framework and lay the foundation for NDK development.

Get to know JNI

1. Why use JNI?

JNI (Java Native Interface, Java Local Interface) is a feature of the Java ecosystem, which extends the capabilities of the Java virtual machine, allowing Java code to interact with C/C++ code. Through the JNI interface, Java code can call C/C++ code, and C/C++ code can also call Java code.

2. The basic process of JNI development

A standard JNI development process mainly includes the following steps:

  • 1. Create HelloWorld.javaand declare the native method sayHi();
  • 2. Use the javac command to compile the source file and generate a HelloWorld.classbytecode file;
  • 3. Use the javah command to export HelloWorld.hthe header file (the header file contains the function prototype of the local method);
  • 4. Implement the function prototype HelloWorld.cppin ;
  • 5. Compile local code and generate Hello-World.sodynamic native library files;
  • 6. Call System.loadLibrary(…) in the Java code to load the so file;
  • 7. Use the Java command to run the HelloWorld program.

JNI template code

JNIEXPORT void JNICALL Java_com_xurui_hellojni_HelloWorld_sayHi (JNIEnv *, jobject);

1. JNI function name

Why does the JNI function name Java_com_xurui_HelloWorld_sayHiadopt the naming method? —— This is the function naming rule of the JNI function static registration convention. There is a one-to-one mapping relationship between Java native methods and JNI functions, and there are two registration methods to establish this mapping relationship: static registration + dynamic registration.

Among them, the static registration is a mapping relationship established based on naming conventions, and the JNI function corresponding to a Java native method will use the agreed function name, ie Java_[类的全限定名 (带下划线)]_[方法名].

2. Keywords JNIEXPORT

JNIEXPORTIt is a macro definition, indicating that a function needs to be exposed to the external use of the shared library. JNIEXPORT is defined differently on Window and Linux:

// Windows 平台 :
#define JNIEXPORT __declspec(dllexport)
#define JNIIMPORT __declspec(dllimport)

// Linux 平台:
#define JNIIMPORT
#define JNIEXPORT  __attribute__ ((visibility ("default")))

3. Keyword JNICALL

JNICALLIt is a macro definition, indicating that a function is a JNI function. JNICALL is defined differently on Windows and Linux:

// Windows 平台 :
#define JNICALL __stdcall // __stdcall 是一种函数调用参数的约定 ,表示函数的调用参数是从右往左。

// Linux 平台:
#define JNICALL

4. The parameter jobject

The jobject type is the JNI layer's representation of the Java layer application type object. Every native method called from Java passes a reference to the current object in the JNI function. Two cases are distinguished:

  • 1. Static native method : The second parameter is jclass type, pointing to the Class object of the class where the native method is located;
  • 2. Instance native method : The second parameter is jobject type, pointing to the object calling the native method.

5. The role of JavaVM and JNIEnv

JavaVMand JNIEnvare the two most critical data structures defined in the jni.h header file:

  • JavaVM : Represents the Java virtual machine. Each Java process has and only has one global JavaVM object, and the JavaVM can be shared across threads;
  • JNIEnv : Represents the Java runtime environment, each Java thread has its own independent JNIEnv object, and JNIEnv cannot be shared across threads.

The type definitions of JavaVM and JNIEnv are slightly different in C and C++, but essentially the same, internally consisting of a series of function pointers pointing to the interior of the virtual machine.

example

1. Create HelloWorld.javaand declare the native method hello_world();

public class JNIExample {
    
    

    static {
    
    
        // 函数System.loadLibrary()是加载dll(windows)或so(Linux)库,只需名称即可,
        // 无需加入文件名后缀(.dll或.so)
        System.loadLibrary("JNIExample");
        init_native();
    }

    private static native void init_native();

    public static native void hello_world();

    public static void main(String...args) {
    
    
        JNIExample.hello_world();
    }
}

2. Use the javac command to compile the source file and generate a HelloWorld.classbytecode file;

javac com/kevin/jni/JNIExample.java
javah com.kevin.jni.JNIExample

3. Use the javah command to export HelloWorld.hthe header file (the header file contains the function prototype of the local method);

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_kevin_jni_JNIExample */

#ifndef _Included_com_kevin_jni_JNIExample
#define _Included_com_kevin_jni_JNIExample
#ifdef __cplusplus
extern "C" {
    
    
#endif
/*
 * Class:     com_kevin_jni_JNIExample
 * Method:    init_native
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_kevin_jni_JNIExample_init_1native
  (JNIEnv *, jclass);

/*
 * Class:     com_kevin_jni_JNIExample
 * Method:    hello_world
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_kevin_jni_JNIExample_hello_1world
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

4. Implement the function prototype HelloWorld.cppin ;

void init_native(JNIEnv *env, jobject thiz) {
    
    
    printf("native_init\n");
    return;
}

void hello_world(JNIEnv *env, jobject thiz) {
    
    
    printf("Hello World!");
    return;
}

static const JNINativeMethod gMethods[] = {
    
    
        {
    
    "init_native", "()V", (void*)init_native},
        {
    
    "hello_world", "()V", (void*)hello_world}
};

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    
    
    __android_log_print(ANDROID_LOG_INFO, "native", "Jni_OnLoad");
    JNIEnv* env = NULL;
    if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) // 从 JavaVM 获取JNIEnv,一般使用 1.4 的版本
        return -1;
    jclass clazz = env->FindClass("me/shouheng/jni/JNIExample");
    if (!clazz){
    
    
        __android_log_print(ANDROID_LOG_INFO, "native", "cannot get class: com/example/efan/jni_learn2/MainActivity");
        return -1;
    }
    if(env->RegisterNatives(clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])))
    {
    
    
        __android_log_print(ANDROID_LOG_INFO, "native", "register native method failed!\n");
        return -1;
    }
    return JNI_VERSION_1_4;
}

5. Compile local code and generate Hello-World.sodynamic native library files;

gcc -c -I"E:\JDK\include" -I"E:\JDK\include\win32" jni/JNIExample.c

6. Call System.loadLibrary(…) in the Java code to load the so file;

gcc -Wl,--add-stdcall-alias -shared -o JNIExample.dll JNIExample.o

7. Use the Java command to run the HelloWorld program.

Guess you like

Origin blog.csdn.net/duoduo_11011/article/details/131299772