JNI/NDK Development Guide (6) -- C/C ++ access Java instance methods and static methods, jnindk
Reprinted please indicate the source: http://blog.csdn.net/xyang81/article/details/42582213
In the previous five chapters, we learned how to use the JNI function to access the basic data types, strings, and arrays in the JVM. Next, we will learn how the local code works with any object in the JVM.AttributeAndMethodInteraction. For example, the local code calls the method or attribute of an object in the Java layer, that is, the callback (callback) from the local function in the C/C ++ layer ). This knowledge point is divided into two articles. This article first introduces method callback and Chapter 7 introduces the attributes of accessing Java by local code.
Before that, let's review the implementation principles in JVM when calling a method in Java, which will help explain the mechanism of local code calling Java method implementation. All the children's shoes that have written Java know that the static method of a class is called directly throughClass Name. MethodIt can be called. This is too simple. What can I do... However, in this call process, JVM has helped us a lot of work. When we are running a Java program, JVM will first use allRelated classThe file is loaded to the JVM and loaded as needed. That is to say, a class is loaded only when used, this design is designed to improve program performance and save memory. Therefore, before calling a static method with the class name, JVM will first determine whether the class has been loaded.ClassLoaderWhen it is loaded to the JVM, the JVM will find the class from the classpath path. if it finds it, it will load it to the JVM and then call the static method of the class. If no exception is found, JVM will throw a java. lang. ClassNotFoundException exception, prompting that this class cannot be found. ClassLoader is a mechanism for JVM to load class bytecode files. If you are not familiar with ClassLoader, please read the article "in-depth analysis of Java ClassLoader principles. In fact, in JNI development, the local code is also based on the above process to compile static methods or instance methods of the class. The following uses an example, describes in detail each step in the process of calling a Java method using local code:
Package com. study. jnilearn;/*** AccessMethod. java * local code example class instance method and static method * @ author yangxin */public class AccessMethod {public static native void callJavaStaticMethod (); public static native void callJavaInstaceMethod (); public static void main (String [] args) {callJavaStaticMethod (); callJavaInstaceMethod ();} static {System. loadLibrary ("AccessMethod ");}}
Package com. study. jnilearn;/*** ClassMethod. java * is used to call local code * @ author yangxin */public class ClassMethod {private static void callStaticMethod (String str, int I) {System. out. format ("ClassMethod: callStaticMethod called! --> Str = % s, "+" I = % d \ n ", str, I);} private void callInstanceMethod (String str, int I) {System. out. format ("ClassMethod: callInstanceMethod called! --> Str = % s, "+" I = % d \ n ", str, I );}}
Header file generated by AccessMethod. class:
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_study_jnilearn_AccessMethod */#ifndef _Included_com_study_jnilearn_AccessMethod#define _Included_com_study_jnilearn_AccessMethod#ifdef __cplusplusextern "C" {#endif/* * Class: com_study_jnilearn_AccessMethod * Method: callJavaStaticMethod * Signature: ()V */JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessMethod_callJavaStaticMethod (JNIEnv *, jclass);/* * Class: com_study_jnilearn_AccessMethod * Method: callJavaInstaceMethod * Signature: ()V */JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessMethod_callJavaInstaceMethod (JNIEnv *, jclass);#ifdef __cplusplus}#endif#endif
Implementation of the function prototype in the local code object:
// AccessMethod. c # include "inline"/** Class: com_study_jnilearn_AccessMethod * Method: callJavaStaticMethod * Signature: () V */JNIEXPORT void JNICALL inline (JNIEnv * env, jclass cls) {jclass clazz = NULL; jstring str_arg = NULL; jmethodID mid_static_method; // 1. Search for the ClassMethod Class from the classpath path and return the Class Object clazz = (* env) of the Class) -> F IndClass (env, "com/study/jnilearn/ClassMethod"); if (clazz = NULL) {return ;} // 2. Find the callStaticMethod method mid_static_method = (* env)-> GetStaticMethodID (env, clazz, "callStaticMethod", "(Ljava/lang/String; I) from the clazz class) V "); if (mid_static_method = NULL) {printf (" the static method callStaticMethod cannot be found. "); Return;} // 3. Call the static callStaticMethod method of the clazz class str_arg = (* env)-> NewStringUTF (env," I am a static method "); (* env)-> CallStaticVoidMethod (env, clazz, mid_static_method, str_arg, 100); // delete a local reference (* env)-> DeleteLocalRef (env, clazz ); (* env)-> DeleteLocalRef (env, str_arg);}/** Class: com_study_jnilearn_AccessMethod * Method: callJavaInstaceMethod * Signature :() V */JNIEXPORT void JNICALL initialize (JNIEnv * env, jclass cls) {jclass clazz = NULL; jobject jobj = NULL; jmethodID mid_construct = NULL; jmethodID mid_instance = NULL; jstring str_arg = NULL; // 1. Search for the ClassMethod Class from the classpath path, and return the Class Object clazz = (* env)-> FindClass (env, "com/study/jnilearn/ClassMethod"); if (clazz = NULL) {printf ("cannot find 'com. study. jnilearn. classMethod 'class "); return;} // 2. Obtain the default constructor ID of the class mid_construct = (* env)-> GetMethodID (env, clazz, "<init>", "() V"); if (mid_construct = NULL) {printf ("default constructor not found"); return ;} // 3. Search for the instance method ID mid_instance = (* env)-> GetMethodID (env, clazz, "callInstanceMethod", "(Ljava/lang/String; I) V "); if (mid_instance = NULL) {return;} // 4. Create an instance of this class, jobj = (* env)-> NewObject (env, clazz, mid_construct ); if (jobj = NULL) {printf ("in com. study. jnilearn. the callInstanceMethod method "); return;} // 5 is not found in the ClassMethod class. The instance method str_arg = (* env) of the calling object-> NewStringUTF (env, "I am an instance method"); (* env)-> CallVoidMethod (env, jobj, mid_instance, str_arg, 200); // delete a local reference (* env) -> DeleteLocalRef (env, clazz); (* env)-> DeleteLocalRef (env, jobj); (* env)-> DeleteLocalRef (env, str_arg );}
Running result:
Code parsing:
AccessMethod. java is the entry of the program. In the main method, the callJavaStaticMethod and callJavaInstaceMethod native methods are called to test native layer calls.In MethodClass. javaCallStaticMethod static method and callInstanceMethod instance method. The return values of these two methods are Void and the parameters are both String and int.
1. callJavaStaticMethod static method implementation
JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessMethod_callJavaStaticMethod(JNIEnv *env, jclass cls)
Locate the 31 rows of AccessMethod. c:
(*env)->CallStaticVoidMethod(env,clazz,mid_static_method, str_arg, 100);
The following is a prototype of the CallStaticVoidMethod function:
void (JNICALL *CallStaticVoidMethod)(JNIEnv *env, jclass cls, jmethodID methodID, ...);
This function receives four parameters:
Env: JNI function table pointer
Cls: Class Object that calls the static method
MethodID: Method ID (because a class contains multiple methods, a unique identifier is required to determine which method of the class to call)
Parameter 4: Method real parameter list
Follow the prompts in the function parameters to complete the callback of the Java static method in the following four steps:
Step 1:Call the FindClass function and input a Class descriptor. JVM searches for the Class from the classpath path and returns the jclass type (used to store Class Object references ). Note that the Class descriptor of ClassMethod is com/study/jnilearn/ClassMethod..(Point) replace all/(Backslash)
(*env)->FindClass(env,"com/study/jnilearn/ClassMethod");
Step 2:Call the GetStaticMethodID function to obtain the callStaticMethod method ID from the ClassMethod class and return the jmethodID type (used to reference the storage method ). The real parameter clazz is the jclass object found in the first step. The real parameter "callStaticMethod" is the method name, and the real parameter "(Ljava/lang/String; I) V" is the signature of the method.
(*env)->GetStaticMethodID(env,clazz,"callStaticMethod","(Ljava/lang/String;I)V");
Step 3:Call the CallStaticVoidMethod function and execute the ClassMethod. callStaticMethod method call. Str_arg and 100 are the real parameters of the callStaticMethod method.
Str_arg = (* env)-> NewStringUTF (env, "I am a static method"); (* env)-> CallStaticVoidMethod (env, clazz, mid_static_method, str_arg, 100 );
Note: JVM defines related functions for the return values of all data types. The preceding callStaticMethod returns Void, so CallStaticVoidMethod is called. Based on different return value types, JNI provides a series of functions with different return values, such as CallStaticIntMethod, CallStaticFloatMethod, CallStaticShortMethod, and CallStaticObjectMethod, the call return values are int, float, short, and Object functions. The reference type calls the CallStaticObjectMethod function. In addition, functions of each return value type provide the following three real parameter types: CallStaticXXXMethod (env, clazz, methodID ,...), callStaticXXXMethodV (env, clazz, methodID, va_list args), CallStaticXXXMethodA (env, clazz, methodID, const jvalue * args), indicating: receives the variable parameter list, receives va_list as the real parameter, and receives the const jvalue * as the real parameter. The following is a prototype of the three real parameters of CallStaticVoidMethod in the jni. h header file:
void (JNICALL *CallStaticVoidMethod) (JNIEnv *env, jclass cls, jmethodID methodID, ...); void (JNICALL *CallStaticVoidMethodV) (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); void (JNICALL *CallStaticVoidMethodA) (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args);
Step 4Release local variables
// Delete a local reference (* env)-> DeleteLocalRef (env, clazz); (* env)-> DeleteLocalRef (env, str_arg );
After the function is completed, JVM Automatically releases the memory space occupied by all locally referenced variables. However, it is safer to manually release the table because a reference table is maintained in the JVM to store local and global reference variables. After testing, the maximum storage space of the table is 512 references, if this number is exceeded, the reference table overflows and the JVM crashes. So it is a good habit to release your application in time!
(Local reference and global reference will be detailed in later articles)
II. Implementation of callInstanceMethod
JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessMethod_callJavaInstaceMethod(JNIEnv *env, jclass cls)
Locate the 43 rows of AccessMethod. c:
(*env)->CallVoidMethod(env,jobj,mid_instance,str_arg,200);
The following is a prototype of the CallVoidMethod function:
void (JNICALL *CallVoidMethod) (JNIEnv *env, jobject obj, jmethodID methodID, ...);
This function receives four parameters:
Env: JNI function table pointer
Obj: The instance that calls this method.
MethodID: Method ID
Parameter 4: real parameter list of the Method
Follow the prompts in the function parameters to complete the callback of the Java static method in the following six steps:
Step 1,Like calling a static method, the FindClass function is used to obtain the Class Object of the Class.
Step 2,Obtain the constructor ID of a class because the object that creates the class first calls the constructor of the class. The default constructor is used as an example.
(*env)->GetMethodID(env,clazz, "<init>","()V");
<Init> indicates the name of the constructor of the class. () V indicates the constructor (default constructor) without any parameters or return values)
Step 3,Call GetMethodID to obtain the callInstanceMethod method ID
(*env)->GetMethodID(env, clazz, "callInstanceMethod", "(Ljava/lang/String;I)V");
Step 4,Call the NewObject function to create an Instance Object of the class.
(*env)->NewObject(env,clazz,mid_construct);
Step 5,Call the CallVoidMethod function and execute the ClassMethod. callInstanceMethod method call. str_arg and 200 are the real parameters of the method.
Str_arg = (* env)-> NewStringUTF (env, "I am an instance method"); (* env)-> CallVoidMethod (env, jobj, mid_instance, str_arg, 200 );
Like JNI calling Java static methods, JVM defines relevant functions (CallXXXMethod) for the return values of all data types, such as CallIntMethod, CallFloatMethod, and CallObjectMethod, it also provides functions that support three types of real parameters. The following uses CallVoidMethod as an example: jni. h. The function prototype in the header file:
void (JNICALL *CallVoidMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...);void (JNICALL *CallVoidMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);void (JNICALL *CallVoidMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);
Step 6,Delete a local reference (remove from the reference table)
// Delete a local reference (* env)-> DeleteLocalRef (env, clazz); (* env)-> DeleteLocalRef (env, jobj); (* env) -> DeleteLocalRef (env, str_arg );
Iii. Method Signature
In the preceding example, A jmethodID parameter must be input to call both static methods and instance methods. Because there is a method overload in Java (the method name is the same and the parameter list is different), it is necessary to clearly tell the JVM which method is called in the class or instance. When calling the JNI GetMethodID function to obtain a jmethodID, You need to input a method name and method signature. The method name is the method name defined in Java,The method signature format is:(Parameter type list) Return Value. In the parameter list, the reference type starts with L, followed by the full path name of the class (replace all with/), ending with a semicolon. The following are some examples:
The ing between the basic Java type and the parameter type and return value type in the method signature is as follows:
For example, the JNI method signature corresponding to String fun (int a, float B, boolean c, String d) is: "(IFZLjava/lang/String;) Ljava/lang/String ;"
Summary:
1. Call the static method and use the CallStaticXXXMethod/V/A function. XXX indicates the Data Type of the returned value. For example: CallStaticIntMethod
2. Call the instance method using CallXXXMethod/V/A function. XXX indicates the returned data type, for example, CallIntMethod.
3. Obtain the ID of an instance method. Use the GetMethodID function to input the method name and signature.
4. Get the ID of a static method. Use the GetStaticMethodID function to pass in the method name and method signature.
5. Obtain the constructor ID. The method name uses "<init>"
6. Get the Class instance of a Class and use the FindClass function to pass in the Class descriptor. JVM searches from the classpath directory.
7. Create a Class instance and use the NewObject function to pass in the Class reference and constructor ID.
8. Delete the local variable reference and use DeleteLocalRef to input the reference variable.
9. Method signature format: (list of parameters) return value type. Note: No space or other characters are required to separate the parameter lists.
10. Class descriptor format: L package name path/class name. Package Names are separated. For example, Ljava/lang/String;
11. Call GetMethodID to obtain the method ID and call FindClass to obtain the Class instance, and make an exception judgment.