preface

The previous two mainly from the overall perspective to explain the communication between Native methods and Java methods and the role of so files, some details are not too detailed, some friends may be some of them are not clear, this article from the most basic JNI syntax with you familiar with, how to write native methods, and what are the differences between Java methods, How do they transfer objects and how do they call them.

Zero base takes you to eat JNI Family bucket (1)

Zero base takes you to eat JNI Family Bucket (2)

This article is long, more basic knowledge in front, directly open code behind, please watch ~

1. JNI syntax

1.1 What are JNIEnv and Jobject?

We will always see these two parameters in native methods, such as the following method

JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)
{
cout<<"Hello World"<<endl;
}
Copy the code

For JNIEnv, this refers to the Java Native Interface Environment, which is a JNI Interface pointer to a function table of Native methods, each member of which points to a JNI function. Native methods access data structures in the JVM through JNI functions, and that is, through this JNIEnv* pointer, they can manipulate Java side code.

For jobject, if the native method is not static, this obj is going to be the class instance of that native method, if the native method is static, this obj is going to be the class object instance of that native method’s class, which class this method is in, Represents an object instance or class instance of this class

1.2 JNI data types

As we all know, there are two data types, eight basic data types and reference types in Java, so there are two corresponding data types in JNI, which refer to two graphs. The specific relationship is as follows:

The basic data types are available directly in the Native layer

Reference data types cannot be used directly. They need to be converted according to the JNI function before being used

Multidimensional arrays (including two-dimensional arrays) are reference types and their values need to be accessed using the jobjectArray type

1.3 Domain descriptors

Basic data types are typically represented with specific uppercase letters

Java classes Type signatures
int I
float F
double D
long J
boolean Z
byte B
char C
short S

A general reference type is L + the class descriptor for that type +; (Note the semicolon “; For example, the String field descriptor is Ljava/lang/String; For arrays, it is: [+ the field descriptor of its type +; int[] the descriptor is [I float[] the descriptor is [F String[] the descriptor is [Ljava/lang/String; Object[] the field descriptor is For example, int [] [] has a descriptor of [[I]

1.4 Method operators

Place the field descriptors of the parameter type in parentheses and the field descriptors of the return value type in the declaration order as follows: (overlay of the field descriptors of the parameter) Return the type descriptor. If there is no return value, use V for void.

For example, String test() corresponds to ()Ljava/lang/String; Note that “;” Do not forget

Int f(int I, Object Object) = ILjava/lang/Object; I

And so on. Be careful. It’s easy to make mistakes

2. JNI Native methods access Java

2.1 Obtaining Methods and Attribute IDS

As stated above, reference data types are not available directly. In the Native layer, it is not practical for you to directly manipulate method properties from Java objects. JNI defines the jfieldID and jmethodID types in the jni.h header file to represent the properties and methods of Java objects. To access or set a Java property, we first need to get the jfieldID representing the Java property in the native code before we can operate on the Java property in the native code

public class Person {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int mAge) {
        age = mAge;
    }

    public String getName() {
        return name;
    }

    public void setName(String mName) { name = mName; }}Copy the code

Let’s say this entity class, let’s say it wants to manipulate the setName method, set some value in there

First, you get this class object, and those of you who are familiar with reflection should see it at a glance

// Obtain the class object jClass clazz_NativeTest=env->FindClass(" com/example/hik/cmake"); // the third argument is the method operator, the argument is String, the return value is null, so it is (Ljava/lang/String;) V jmethodID id_show = env - > GetMethodID (clazz_NativeTest, "elegantly-named setName","(Ljava/lang/String;) V"); JfieldID jfieldID1 = env->GetFieldID(student,")name","Ljava/lang/String;Char *c_new_name = "char *c_new_name ="lisi";
jstring str = env->NewStringUTF(c_new_name);
env->CallVoidMethod(person, id_show, str);
Copy the code

2.2 Creating Java Objects locally

JNIEnv provides the following methods to create a Java object:

jobject NewObject(jclass clazz, jmethodID methodID,…) ;

jobject NewObjectV(jclass clazz, jmethodIDmethodID,va_list args);

jobject NewObjectA(jclass clazz, jmethodID methodID,const jvalue *args) ;

The functions that create Java objects locally are similar to those that call Java methods locally:

The first argument, jClass class, represents the object of which class you want to create

The second argument, jmethodID, methodID, indicates which constructor ID you want to use to create the object.

As long as we have jClass and jmethodID, we can create objects of this Java class in our local methods.

Refers to: Env ->GetMethodID(clazz,method_name,sig);}} The default Java constructor has no return value and no arguments. Such as:

jclassclazz=env->FindClass("java/util/Date"); JmethodID id_date=env->GetMethodID(clazz,"<init>"."()V"); JmethodID jobject date=env->NewObject(clazz,id_date); // Call the NewObject method to create the java.util.Date objectCopy the code

2.3 Example Code

2.3.1 Changing Java Object Properties

public class Person {
    private int age;
    private String name;
    public Person() {
    }
    public Person(int mAge, String mName) {
        age = mAge;
        name = mName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int mAge) {
        age = mAge;
    }

    public String getName() {
        return name;
    }

    public void setName(String mName) {
        name = mName;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\''+'}'; }}Copy the code

In the Java layer, we create a new entity class Bean to manipulate the communication, and then add a native method, changePersonName

public class NativeHelper  {
    static {
        System.loadLibrary("native-lib");
    }
    public  native String stringFromJNI();
    public  native int add(int a,int b);

    public  native void changePersonName(Person mPerson);

   public native Person getStudent();

   public native List<Person> getPeronList();
}
Copy the code

Native method, we based on the first article, to add a method

// Call the Java layer object, Void changeName(JNIEnv *env, jobject Instance, Jobject person) {// get person class object jClass student = env->GetObjectClass(person); / / to getsetName Indicates the id of the method jmethodIDsetNameMethond = env->GetMethodID(student, "setName"."(Ljava/lang/String;) V");
    char *c_new_name = "lisi"; jstring str = env->NewStringUTF(c_new_name); Env ->CallVoidMethod(person, person, person);setNameMethond, str);
}
Copy the code

Remember to add the method in dynamic registration

JNINativeMethod jniNativeMethod[] = {{"stringFromJNI"."()Ljava/lang/String;",                       (void *) backStringToJava},
                                         {"add"."(II)I",                                      (void *) addNum},
                                         {"changePersonName"."(Lcom/example/taolin/jni_project/Person;) V", (void *) changeName}};
Copy the code

The name has been changed to “lisi”, the main interface code is not pasted, directly call native method is good

2.3.2 Return Java layer entity objects

Let’s add one more method

public native Person getStudent();
Copy the code

Native method, also add one

// Return the Java layer object jobjectreturnJclass jclass1 = env->FindClass(Person(JNIEnv *env, jobject instance) {Person(JNIEnv *env, jobject instance) {"com/example/taolin/jni_project/Person"); JmethodID jmethodID jmethodID1 = env->GetMethodID(jclass1, jmethodID jmethodID jmethodID1)"<init>"."(ILjava/lang/String;) V");
    jint age = 20;
    char *back_name = "wangwu"; jstring str = env->NewStringUTF(back_name); Jobject perosn = env->NewObject(jclass1, jmethodID1, age, STR); jobject perosn = env->NewObject(jclass1, jmethodID1, age, STR);return perosn;
}
Copy the code

Dynamic registration association

    JNINativeMethod jniNativeMethod[] = {{"stringFromJNI"."()Ljava/lang/String;",                       (void *) backStringToJava},
                                         {"add"."(II)I",                                      (void *) addNum},
                                         {"changePersonName"."(Lcom/example/taolin/jni_project/Person;) V", (void *) changeName},
                                         {"getStudent"."()Lcom/example/taolin/jni_project/Person;",  (void *) returnPerson}};
Copy the code

The main page directly calls getStudent(), finds that returns a student object, name “wangwu”, native layer returns the object as a success

2.3.3 Native returns list objects to Java

Add a method

public native List<Person> getPeronList();
Copy the code

Here, native layer, corresponding to add

// Return a list jobject to the Java layerreturnJclass jclass1 = env->FindClass(JNIEnv *env, jobject instance) {"java/util/ArrayList"); JmethodID contructMethod = env->GetMethodID(jclass1, jmethodID contructMethod = env->GetMethodID(jclass1,"<init>"."()V"); Jobject list = env->NewObject(jclass1,contructMethod); JmethodID methodAdd = env->GetMethodID(jclass1,"add"."(Ljava/lang/Object;) Z"); Jclass studentClass = env->FindClass(jclass studentClass = env->FindClass("com/example/taolin/jni_project/Person"); // Get person jmethodID jmethodID jmethodID1 = env->GetMethodID(studentClass,"<init>"."(ILjava/lang/String;) V");
    for(int i =0; i<4; i++){ jobject person = env->NewObject(studentClass,jmethodID1,i,env->NewStringUTF("tl")); CallBooleanMethod env->CallBooleanMethod(list,methodAdd,person); }return list;
}
Copy the code

Finally, don’t forget to register bindings

 JNINativeMethod jniNativeMethod[] = {{"stringFromJNI"."()Ljava/lang/String;",                       (void *) backStringToJava},
                                         {"add"."(II)I",                                      (void *) addNum},
                                         {"changePersonName"."(Lcom/example/taolin/jni_project/Person;) V", (void *) changeName},
                                         {"getStudent"."()Lcom/example/taolin/jni_project/Person;",  (void *) returnPerson},
                                         {"getPeronList"."()Ljava/util/List;",                         (void *) returnList}};
Copy the code

GetPeronList () returns a list of 4 lengths

3. Summary

JNI learning will temporarily come to an end, because I am also new to this area, let me talk about how deep, I am also strong but not enough, because the C++ learning is not too good, so I dare not harm people’s children, but still hope to help some people who are ready to learn JNI development.

Inside the pit is actually quite a lot, so small partners must begin to operate, build an environment, write some code, and finally must be something to gain, have doubts or ideas of friends can leave a message to discuss, than the heart! ~