“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”
This article has been published in the public number [Android development programming]
Preface subtotal
1. The basic knowledge of JNI and NDK has been talked about in the previous article. If you don’t understand, you can see it on the homepage of the public account.
2, jNI common methods such as: class, method, array, string, etc.
This article explains how to register functions in jni and how to call Java from c++.
JNI function registration
1, JNI function annotation knowledge introduction
JNI technology is the communication bridge between Java world and Native world. In terms of code, how does Java layer code call Native layer code? As we all know, before the call native methods, the first thing to call System. LoadLibrary interface to load an implementation of a dynamic library of native method can normal visit, otherwise you will be thrown. Java lang. UnsatisfiedLinkError anomalies. So, when calling a native method in Java, how can the JVM correctly find that native function implemented in C/C++?
The JVM looks for native methods in two ways;
Register the local functions into the JVM by calling the RegisterNatives function provided by JNI according to the JNI specification naming rules.
In the first way, you can use the Javah tool to automatically generate JNI native C/C++ header files according to the native methods defined in the Java classes and the naming rules of the JNI specification.
The second method requires the RegisterNatives call in the JNI_OnLoad function of the local library to register dynamically.
JNI function registration is the implementation of binding Native methods declared by the Java layer to the actual Native functions, that is, the Java layer can directly call the defined Native methods as long as the local place is registered through the JNI function registration mechanism. Corresponding to the above two ways for THE JVM to find native methods, JNI function registration is generally divided into static registration and dynamic registration.
2. Static registration
Principle: Establish one-to-one correspondence between Java method and JNI function according to function name;
Implementation process:
Write Java code;
Use javah directive to generate the corresponding.h file;
Implement the declaration in.h;
Disadvantages:
Writing is not convenient, JNI method names must follow rules and names are long;
Writing process steps, inconvenient;
Program running efficiency is low, because the first call to a native function needs to search for the corresponding local function in the JNI layer according to the function name, and then establish the corresponding relationship, which is time-consuming.
public class Test {
static {
System.loadLibrary(“native-lib”);
}
public native String textFromJni();
}
Use Javah to generate the corresponding local method header file.
#include <jni.h>
#ifndef _Included_test
#define _Included_test
#ifdef __cplusplus
extern “C” {
#endif
JNIEXPORT jstring JNICALL Java_test_Test_textFromJni
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
3. Dynamic registration
In contrast to the static registration method above, there is another way to dynamically register JNI functions, called dynamic registration. Dynamic registration is when the Java layer calls the System.loadLibrary method to load the so library, the JNI_OnLoad function of the local library is called, and RegisterNatives function is called in the JNI_OnLoad function to complete the registration of the local method.
Principle: Use the RegisterNatives method to register one-to-one mappings between Java methods and JNI functions;
Implementation process:
The structure JNINativeMethod array is used to record the corresponding relationship between Java methods and JNI functions.
Implement the JNI_OnLoad method, after loading the dynamic library, the implementation of dynamic registration;
Call the FindClass method to get the Java object;
To complete the registration, call the RegisterNatives method, passing in Java objects and the JNINativeMethod array and the number of registrations;
Advantages:
The process is more clear and controllable;
More efficient;
The JNINativeMethod structure describes the local method structure and is defined as follows:
typedef struct {
const char* name; // Java method name
const char* signature; // Java method signature
void* fnPtr; // Function pointer to the jni native method
} JNINativeMethod;
The first argument to the structure, name, is the Name of the Java method;
The second parameter, signature, describes the method’s parameters and return values;
The third argument, fnPtr, is a function pointer to the jNI function;
The second parameter signature uses a string to record the parameter and return value of the method. The format is “()V” and “(II)V”. The parenthesis indicates the parameter and the right parenthesis indicates the return value.
① Data type mapping
Basic data type
②. Array reference type
If it is a one-dimensional array, follow the table below. If it is a two-dimensional array or higher, the corresponding native type is jobjectArray, and the number of ‘[‘ is used in the field descriptor to indicate the number of dimensions
③. Object reference types
For other reference types, objects in Java, the mapping rule is
4. Object array reference type
If it is a one-dimensional array, follow the table below. If it is a two-dimensional array or higher, the corresponding native type is jobjectArray, and the number of ‘[‘ is used in the field descriptor to indicate the number of dimensions
Define the local methods in a Java file and load the local SO library
package test.jnitest;
public class Test {
static {
System.loadLibrary(“native-lib”);
}
public native String textFromJni();
}
Register the local method in the JNI_OnLoad function
jstring textFromJni(JNIEnv* env, jobject thiz) {
return env->NewStringUTF(“text from jni”);
}
static JNINativeMethod gMethods[] = {
{“textFromJni”, “()Ljava/lang/String;” , (void*)textFromJni}
};
int registerMethod(JNIEnv *env) {
jclass test = env->FindClass(“cc/ccbu/jnitest/Test”);
return env->RegisterNatives(test, gMethods, sizeof(gMethods)/ sizeof(gMethods[0]));
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) ! = JNI_OK) {
return JNI_ERR;
}
if (registerMethod(env) ! = JNI_OK) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
Note:
At the end of the JNI_OnLoad function, we must have a return value, and it must be either JNI_VERSION_1_4 or JNI_VERSION_1_6, which is the version number of JNI. We must return the correct version number, otherwise the system cannot load.
4, c++ call Java detailed explanation
(1) Find the corresponding Java Class
(2) Find the methodID of the method to be called
(3) Call the corresponding method in C language
(1) Create the same class objects using the native methods of the JAVA layer
Steps:
I. Obtain the class from the object
II. Get the ID of the constructor of the class from the class
III. Create a new object based on the method ID and class
JNIEXPORT void JNICALL JAVA_nativeMethod
(JNIEnv *env, jobject thiz,jint i){
.
jclass clazz = (*env).GetObjectClass(thiz);
jmethodID mid = (*env).GetMethodID(clazz,””,”()V”);
jobject obj = (*env).NewObject(clazz,mid);
.
return;
}
Create objects of different classes in C/C++
Steps:
I. Use the FindClass method to obtain the required class
II. Get the ID of the constructor of the class from the class
III. Create a new object based on the method ID and class
JNIEXPORT void JNICALL JAVA_nativeMethod
(JNIEnv *env, jobject thiz,jint i){
.
jclass clazz = (*env).FindClass(“com/x/test/Test”); // The parameter is the classpath
jmethodID mid = (*env).GetMethodID(clazz,””,”()V”);
jobject obj = (*env).NewObject(clazz,mid);
.
return;
}
③ Get the context JNIEnv
JNIEnv gets the context if it cannot be found
bool AttachCurrentThread(JavaVM* vm, JNIEnv** p_env){ bool bAttached = false; switch(vm->GetEnv((void**)p_env, JNI_VERSION_1_4)) { case JNI_OK: break; case JNI_EDETACHED: if (vm->AttachCurrentThread(p_env, 0) < 0) { LOGD("%s :test failed!" ,__func__); return false; } else { bAttached = true; } break; case JNI_EVERSION: LOGE("Invalid java version"); break; } return bAttached; }Copy the code
conclusion
The above summary of JNI function registration in the two methods, in the actual application are very common are used, to understand in place to be able;
The next article will continue with the knowledge and advanced applications of JNI