In the previous two articles, WE introduced the basic types, strings, and arrays operated by Android through JNI, and described the conversion relationship between Java and Native between types and signatures.

With those foundations, Java and Native can be implemented to call each other, in Native to access the fields of the Java class and call the corresponding methods.

Access to the field

Native methods access Java fields in two forms: instance fields and static fields of a class.

In either case, the first step is to define a concrete Java type, with both the instance and static field types and methods.

public class Animal { protected String name; public static int num = 0; public Animal(String name) { this.name = name; } public String getName() { return this.name; } public int getNum() { return num; }}Copy the code

Access the instance field of the class

To access a Java class field, do the following:

  1. Gets the class of a Java object
  2. Gets the ID of the corresponding field
  3. Get the name field from the Animal class and modify it as an example:
private native void accessInstanceFiled(Animal animal);
Copy the code

The corresponding C++ code is as follows:

extern "C" JNIEXPORT void JNICALL Java_com_glumes_cppso_jnioperations_FieldAndMethodOps_accessInstanceFiled(JNIEnv *env,jobject instance, jobject animal) { jfieldID fid; // The id of the field you want to get jstring JSTR; Const char * STR; Jclass CLS = env->GetObjectClass(animal); Fid = env->GetFieldID(CLS, "name", "Ljava/lang/String; ); If (fid == NULL) {// If fid == NULL, return; } jstr = (jstring) env->GetObjectField(animal, fid); STR = env->GetStringUTFChars(JSTR, NULL); if (str == NULL) { return; } LOGD("name is %s", str); env->ReleaseStringUTFChars(jstr, str); jstr = env->NewStringUTF("replaced name"); if (jstr == NULL) { return; } env->SetObjectField(animal, fid, jstr); // Change the value of the field}Copy the code

In the above code, we first get the corresponding Java Class using the GetObjectClass function, which takes the object type jobject, and the result is a value of type JClass, representing the Java Class type.

The next step is to get the field ID of the Java type using the GetFieldID method. Among them, the first parameter is the Java type obtained before, the second parameter is the specific name of the field in Java, and the third parameter is the specific type corresponding to the field. The signature description of this type should be converted into the Native representation, that is, the signature conversion between Java and Native mentioned before.

Once you have the Java type and field ID, you can get the specific value using the GetObjectField method, which takes the previously obtained Java type and the field ID.

The GetObjectField method has many forms. For field values that refer to types, the general rule is GetObjectField, and then the result is converted to the desired type. For base types, there are corresponding methods, such as GetBooleanField, GetIntField, GetDoubleField, and so on.

Once you have the value of the field, you can do what you want.

Finally, you can modify the corresponding value of a field using the SetObjectField method. The first two parameters are the corresponding Java type and field ID, and the last parameter is the specific value. This method is also for the field type is a reference type, and for the base type, there are corresponding methods. Such as SetBooleanField, SetCharField, and SetDoubleField.

Access the static fields of the class

To access static fields of a class, the steps are similar to those for instance fields of a class:

 private native void accessStaticField(Animal animal);
Copy the code

The corresponding C++ code is as follows:

 extern "C"
 JNIEXPORT void JNICALL
 Java_com_glumes_cppso_jnioperations_FieldAndMethodOps_accessStaticField(JNIEnv *env, jobject instance,jobject animal) {
     jfieldID fid;
     jint num;
     jclass cls = env->GetObjectClass(animal);
     fid = env->GetStaticFieldID(cls, "num", "I");
     if (fid == NULL) {
         return;
     }
     num = env->GetStaticIntField(cls, fid);
     LOGD("get static field num is %d", num);
     env->SetStaticIntField(cls, fid, ++num);
 }
Copy the code

The biggest difference between static and instance field access of a class is the corresponding method for JNI calls. For Static fields of a class, JNI’s methods use the Static flag to indicate that this corresponds to Static field access of the class.

The method call

JNI calls Java methods and JNI accesses Java fields in much the same way,

  1. Gets the class of a Java object
  2. Gets the ID of the corresponding method
  3. Calling concrete methods Take the example of calling instance and static methods of a class:

Invoke the instance method of the class

JNI invokes instance methods of Java classes

private native void callInstanceMethod(Animal animal);
Copy the code

The corresponding C++ code is as follows:

// Extern "C" JNIEXPORT void JNICALL Java_com_glumes_cppso_jnioperations_FieldAndMethodOps_callInstanceMethod(JNIEnv *env, jobject instance,jobject animal) { jclass cls = env->GetObjectClass(animal); JmethodID = env->GetMethodID(CLS, "callInstanceMethod", "(I)V"); If (mid == NULL) {return; } env->CallVoidMethod(animal, mid, 2); // Call method}Copy the code

The GetFieldID method is replaced by the GetMethodID method, and the CallVoidMethod function calls the specific method. The first two arguments are the obtained class and method ID, and the last argument is the specific method call parameter.

The first parameter of the GetMethodID method is the specific Java type, the second parameter is the name of the corresponding instance method of the Java class, and the third parameter is the corresponding return type of the method and the description of the parameter signature converted to Native.

For functions that do not need to return a value, call CallVoidMethod; for functions that return a reference type, call CallObjectMethod; for methods that return a base type, there are corresponding method calls. For example: CallBooleanMethod, CallShortMethod, CallDoubleMethod, etc.

Call a static method of a class

Static methods that call classes are similar to instance methods that call classes:

private native void callStaticMethod(Animal animal);
Copy the code

The corresponding C++ code is as follows:

// extern "C" JNIEXPORT void JNICALL Java_com_glumes_cppso_jnioperations_FieldAndMethodOps_callStaticMethod(JNIEnv *env,jobject instance, jobject animal) { jclass cls = env->GetObjectClass(animal); jmethodID argsmid = env->GetStaticMethodID(cls, "callStaticMethod", "(Ljava/lang/String;) Ljava/lang/String;" ); if (argsmid == NULL) { return; } jstring jstr = env->NewStringUTF("jstring"); env->CallStaticObjectMethod(cls, argsmid, jstr);Copy the code

Call the class’s static method callStaticMethod, which takes a String argument and returns a String argument.

The procedure is similar to that of a class instance method, except that the method name is Static.