What is JNI and how to use it
Jni--java Native Interface, which is a feature of the Java platform (not specific to the Android system). In fact, the main definition of a number of JNI functions, so that developers can call these functions to implement Java code C + + code, the code can also call Java code, C + +, so you can play the characteristics of each language. So how to use JNI, in general, we will be the first to compile the C + + code to the corresponding platform of the dynamic library (Windows is generally DLL files, Linux is generally so files, etc.), here we are for the Android platform, so only discuss so library. Because JNI programming supports C and C + + programming, here our chestnuts are using C + +, for the C version may be somewhat different, but the main content is consistent, we can comprehend by analogy.
To give a simple example
This is also directly from the code, so that more image and intuitive, but also easy to understand. The Java code used today is as follows:
public class Androidjni { static{ system.loadlibrary ("main"); } public native void Dynamiclog (); public native void Staticlog ();}
Here we define two methods declared as native, and declare a static zone, in which the static zone class is loaded with a library named libmain.so, here we say is the libmain.so library, but the load when it only wrote "main", in fact, as long as we know that this is agreed on it.
The C + + code is as follows:
#include <jni.h> #define LOG_TAG "main.cpp" #include "mylog.h" static void Nativedynamiclog (JNIEnv *evn, Jobject obj { LOGE ("Hell Main");} Jniexport void Jnicall Java_com_github_songnick_jni_androidjni_staticlog (jnienv *env, Jobject obj) { LOGE ("Static Register log "); Jninativemethod nativemethod[] = {{"Dynamiclog", "() V", (void*) Nativedynamiclog},}; Jniexport jint jnicall jni_onload (JAVAVM *jvm, void *reserved) { jnienv *env; if (jvm->getenv (void**) &env, jni_version_1_4)! = JNI_OK) { return-1; } LOGE ("Jni_onload comming"); Jclass CLZ = Env->findclass ("Com/github/songnick/jni/androidjni"); Env->registernatives (CLZ, Nativemethod, sizeof (Nativemethod)/sizeof (nativemethod[0])); return jni_version_1_4;}
Here's a reference to two header files, jni.h and mylog.h, where jni.h defines a lot of the JNI functions and structs we use, and mylog.h is my own definition of the function that prints the Android log (the same functionality as the Java Log Class).
Some of the specifications for compiling so libraries and so libraries are not discussed here for the time being. This assumes that the above C + + program is compiled into a file called libmain.so. Use the System.loadlibarary ("main") method at the Java layer to load the so library so that Dynamiclog (), Staticlog (), and the corresponding java_com_github_songnick_jni_ Androidjni_staticlog (), Nativedynamiclog () Two native methods link up, of course, this part of the work is done by the Java Virtual machine, then the specific how to complete, the next will be based on the above code for analysis.
Static Registration native method
In the above code, see the Jniexport and Jnicall keywords, these two keywords are two macro definitions, his main role is to explain that the function is a JNI function, when the Java virtual machine is loaded, the corresponding native method is linked, Staticlog () is declared in the Androidjni.java class as the native method, and his corresponding JNI function is Java_com_github_songnick_jni_androidjni_staticlog (), So how to link, when the Java Virtual machine load so library, if found to contain the above two macro definition of the function will be linked to the corresponding Java layer of the native method, then how to know which class in Java which native method? We carefully observed that the composition of the JNI function name is: Java_pkgname_classname_nativemethodname, prefixed with Java, and with "_" underscore the package name, class name and native method name is the corresponding JNI function. In general, we can manually to follow this rule to write, but if the native method is particularly many, then there is a certain amount of work, and in the process of writing accidentally may write wrong, in fact, Java provides us with Javah tools to help generate the corresponding header file. In the generated header file is the corresponding JNI function generated in accordance with the above-mentioned rules, we are in the development of a direct copy of the past can be. Here is the code above for example, after compiling in Androidstudio, go to the project directory App/build/intermediates/classes/debug, run the following command:
javah-d JNI Com.github.songnick.jni.AndroidJni
Here-d Specifies the directory where the. h file is generated (if it is not created automatically), Com.github.songnick.jni.AndroidJni represents the class file under the specified directory. Here is a brief introduction to the generated JNI function contains two fixed parameter variables, respectively, jnienv and Jobject, where JNIEnv is described later, Jobject is the currently linked native method of the class object (similar to this in Java). Both of these variables are generated by the Java Virtual machine and passed in at the time of invocation.
Dynamic registration
Above we introduce the process of static registration native method, that is, the Java Layer declaration of the native method and the JNI function is one by one corresponding, then there is no way to let Java layer native method and arbitrary jni function link up, of course, it is possible, this must use the method of dynamic registration. The next step is to see how to implement dynamic registration.
Jni_onload function
When we use the System.loadlibarary () method to load the so library, the Java Virtual machine will find this function and call the function, so you can do some initialization action in the function, in fact, this function is equivalent to the activity of the OnCreate () Method. The function is preceded by three keywords, jniexport, jnicall, and Jint, where Jniexport and Jnicall are two macro definitions that specify that the function is a JNI function. Jint is a data type that is defined by JNI, because the data types of Java and C + + or objects cannot be referenced or used directly from each other, the JNI layer defines its own data type, which is used to interface the Java layer and the JNI layer, as described in the following data types. The jint here corresponds to the Java int data type, which returns an int representing the version of the JNI that is currently in use, in fact, similar to the API version of the Android system, some of the different JNI functions defined in the different JNI versions. The function will have two parameters, where *JVM is a Java Virtual machine instance, and the JAVAVM struct defines the following function:
Destroyjavavmattachcurrentthreaddetachcurrentthreadgetenv
Here we use the getenv function to get the JNIENV variable, which has the following code in the Jni_onload function:
JNIEnv *env;if (Jvm->getenv ((void**) &env, jni_version_1_4)! = JNI_OK) { return-1;}
Here called the getenv function to get the jnienv struct pointer, in fact, the jnienv struct is pointing to a function table, the function table points to the corresponding JNI function, we call these JNI functions to implement JNI programming, we will also introduce it later.
Get Java objects, complete dynamic registration
The above describes how to get the jnienv struct pointer, we can call the Registernatives function in jnienv to complete the dynamic registration native method After we get the struct pointer. The method is as follows:
Jint registernatives (Jclass clazz, const jninativemethod* methods, Jint nmethods)
The first argument is that the Java layer corresponds to the object that contains the native method (this is the Androidjni object), and the class object is obtained by calling the jnienv corresponding function (the parameter of the Findclass function is to obtain the classes descriptor of the class object):
Jclass CLZ = Env->findclass ("Com/test/jni/androidjni");
The second parameter is the Jninativemethod struct pointer, where the Jninativemethod struct is a description of the Java layer native method, which is defined as follows:
typedef struct { const char* Name;//java Layer Native method name const char* Signature;//java Layer Native method descriptor void* fnptr;//pointer to JNI function} Jninativemethod;
The third parameter is the number of registered native methods. It is common to dynamically register multiple native methods, first defining a Jninativemethod array, and then passing the array pointer as a parameter to the Registernative function, so the following Jninativemethod array is defined:
Jninativemethod nativemethod[] = {{"Dynamiclog", "() V", (void*) Nativedynamiclog}};
Finally, call the Registernative function to complete the dynamic registration:
Env->registernatives (CLZ, Nativemethod, sizeof (Nativemethod)/sizeof (nativemethod[0]));
JNIEnv Structural Body
The above mentioned JNIENV this structure, it is very old, point to a function table, the function table points to a series of JNI functions, we call these JNI functions can be implemented with the Java layer of interaction, here is a simple look at a few defined functions:
Jfieldid Getfieldid (Jclass clazz, const char* name, const char* SIG) Jboolean Getbooleanfield (Jobject obj, Jfiel Did FieldID) Jmethodid Getmethodid (Jclass clazz, const char* name, const char* SIG) Callvoidmethod (Jobject obj, jmethodid me Thodid, ...) Callbooleanmethod (Jobject obj, Jmethodid methodid, ...) ..........
Here is a simple look at the above four functions, the Getfieldid () function is to get a variable in a Java object, the Id,getbooleanfield () function is to obtain a variable with the data type Boolean based on the ID of the variable. The Getmethodid () function is a method that gets the Id,callvoidmethod () of the corresponding method in the Java object, based on Methodid, and the return value of the method is of type void. With these functions we can implement code that calls the Java layer.
Android JNI Programming-jni Basics