Android JNI use summary

In this article, we skip the underlying mechanism of JNI, the reader is best to think of it as the glue between native code and java code.

In layman's terms, JNI is a technology through which the following two points can be achieved:
· A function in a Java program can call a function written in Native language. Native generally refers to a function written in C/C++.
· The function in the Native program can call the function of the Java layer, that is, the function of Java can be called in the C/C++ program.

Steps to call C/C++ library in Android:

  • The first step: introduce the C code library name through System.loadLibrary.
  • Step 2: Write C/C++ code in natice-lib.cpp in the cpp directory.
  • Step 2: Call the corresponding implementation method in the C/C++ file.

JNI / NDK 的 API

         To access the Java-side code in the C/C++ native code, a common application is to obtain the attributes of the class and call the method of the class. In order to express the attributes and methods in C/C++, JNI defines jfieldID in the jni.h header file, The jmethodID type represents the attributes and methods on the Java side, respectively. When accessing or setting a Java property, you must first obtain the jfeldID representing the Java property in the local code, and then perform the Java property operation in the native code. Similarly, when you need to call a method on the Java side, you also need to obtain the representative of the method. The jmethodID can be used for Java method calls. Next, try how to call a method in Java in native.

JNIEnv type and jobject type

JNIEnv *env and jobjet instance, briefly introduce the role of these two types.

JNIEnv type

The JNIEnv type actually represents the Java environment, and the code on the Java side can be manipulated through the JNIEnv* pointer. For example, we can use JNIEnv to create objects in Java classes, call methods of Java objects, and obtain properties in Java objects.

There are many functions available in the JNIEnv class , as shown below:

  • NewObject: Create an object in the Java class.
  • NewString: Create a String object in the Java class.
  • NewArray: Create an array object of type Type.
  • GetField: Get the field of type Type.
  • SetField: Set the value of a field whose type is Type.
  • GetStaticField: Get the static field of type Type.
  • SetStaticField: Set the value of the static field of type Type.
  • CallMethod: Call the method whose return type is Type.
  • CallStaticMethod: Call the static method whose return value type is Type.
    Of course, in addition to these commonly used functions, there are more functions that can be used, which can be viewed in the jni.h file, or refer to https://docs.oracle.com/javase/6/docs/technotes/guides /jni/spec/jniTOC.html link to query related methods, the above is very clear.

Okay, after talking about JNIEnv, let's talk about the second jobject.

jobject type

Jobject can be regarded as a reference to a class instance in java. Of course, different situations have different meanings. If the native method is not static, obj represents the class instance of the native method.

If the native method is static, obj represents the class object instance of the native method class (the static method does not require a class instance, so it represents the class object of this class).

The syntax for calling methods in Java in C language is similar to reflection in Java. Object mapping in Java is represented by jobject in C language.

To give a simple example: we create a static method testStaticCallMethod and a non-static method testCallMethod in TestJNIBean. How do we write in the cpp file?

TestJNIBean的代码:

 public class TestJNIBean{
    public static final String LOGO = "learn android with aserbao";
    static {
        System.loadLibrary("native-lib");
     }
    public native String testCallMethod();  //非静态
 
    public static native String testStaticCallMethod();//静态
 
    public  String describe(){
        return LOGO + "非静态方法";
    }

    public static String staticDescribe(){
        return LOGO + "静态方法";
    }
}
cpp文件中实现:

 extern "C"
 JNIEXPORT jstring JNICALL
 Java_com_example_androidndk_TestJNIBean_testCallMethod(JNIEnv *env, jobject instance) {
    jclass  a_class = env->GetObjectClass(instance);                                   //因为是非静态的,所以要通过GetObjectClass获取对象
    jmethodID  a_method = env->GetMethodID(a_class,"describe","()Ljava/lang/String;");// 通过GetMethod方法获取方法的methodId.
    jobject jobj = env->AllocObject(a_class);                                         // 对jclass进行实例,相当于java中的new
    jstring pring= (jstring)(env)->CallObjectMethod(jobj,a_method);                 // 类调用类中的方法
    char *print=(char*)(env)->GetStringUTFChars(pring,0);                           // 转换格式输出。 
    return env->NewStringUTF(print);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_androidndk_TestJNIBean_testStaticCallMethod(JNIEnv *env, jclass type) {
    jmethodID  a_method = env->GetMethodID(type,"describe","()Ljava/lang/String;"); // 通过GetMethod方法获取方法的methodId.
    jobject jobj = env->AllocObject(type);                                          // 对jclass进行实例,相当于java中的new
    jstring pring= (jstring)(env)->CallObjectMethod(jobj,a_method);                 // 类调用类中的方法
    char *print=(char*)(env)->GetStringUTFChars(pring,0);                           // 转换格式输出。
    return env->NewStringUTF(print);
}

The biggest difference between the above two methods is that the static method will be directly passed into the jclass, so we can omit the step of obtaining the jclass, and the non-static method passes in the current class 

GetFieldID是得到java类中的参数ID,GetMethodID得到java类中方法的ID,它们只能调用类中声明为 public的参数或方法。使用如下:

jfieldID topicFieldId = env->GetFieldID(objectClass,"name", "Ljava/lang/String;");
jmethodID getcName=env->GetMethodID(objectClass,"getcatName","()Ljava/lang/String;");

The first parameter is a Java class object. The second parameter is the parameter (or method name), and the third parameter is the signature of the parameter (or method). The third parameter is obtained by the following method.

Calling Java code in C/C++

How to get classes in Java and generate objects

There are several methods in the JNIEnv class to get the classes in java:

  • jclass FindClass(const char* name) Find a class according to the class name, the complete class name
What we need to pay attention to is that the FindClass method parameter name is the full path of a certain class. For example, if we want to call the getTime method of the Date class in Java, then we can do this:

 

 extern "C"
 JNIEXPORT jlong JNICALL
 Java_com_example_androidndk_TestJNIBean_testNewJavaDate(JNIEnv *env, jobject instance) {
     jclass  class_date = env->FindClass("java/util/Date");//注意这里路径要换成/,不然会报illegal class name
     jmethodID  a_method = env->GetMethodID(class_date,"<init>","()V");
     jobject  a_date_obj = env->NewObject(class_date,a_method);
     jmethodID  date_get_time = env->GetMethodID(class_date,"getTime","()J");
     jlong get_time = env->CallLongMethod(a_date_obj,date_get_time);
     return get_time;
}
  • jclass GetObjectClass(jobject obj) According to an object, get the object's class

How to call Java methods in C/C++?

In the JNIEnv environment, we have the following two methods to obtain methods and attributes:

  • GetMethodID: Get the ID of a non-static method;
  • GetStaticMethodID: Get the ID of the static method;
    to get the corresponding jmethodID.

The GetMethodID method is as follows:

1  jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)

The parameter description of the method:

  • clazz: The class object of the class object that this method depends on.
  • name: The name of this field.
  • sign: The signature of this field (each variable and each method has a corresponding signature).

Modify Java variables in C/C++

The idea of ​​modifying the corresponding variables in Java is actually very simple.

  • Find the corresponding class object.
  • Find the attributes in the class that need to be modified
  • Re-assign properties in the class
代码如下:

 public class TestJNIBean{
     static {
         System.loadLibrary("native-lib");
     }
      public int modelNumber = 1;
     /**
      * 修改modelNumber属性
      */
     public native void testChangeField();
}

/*
 * 修改属性
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_example_androidndk_TestJNIBean_testChangeField(JNIEnv *env, jobject instance) {
    jclass  a_class = env->GetObjectClass(instance);                // 获取当前对象的类
    jfieldID  a_field = env->GetFieldID(a_class,"modelNumber","I"); // 提取类中的属性
    env->SetIntField(instance,a_field,100);                         // 重新给属性赋值
}
调用testChangeField()方法后,TestJNIBean中的modelNumber将会修改为100。

Manipulate Java arrays in C/C++

jType* GetArrayElements((Array array, jboolean* isCopy)): This kind of method can convert the basic type array of Java into an array in C/C++ . When isCopy is true, it means that the data will be copied, and the pointer of the returned data is the pointer of the copy. If it is false, it will not be copied and the pointer of Java data will be used directly. Not applicable isCopy can pass NULL or 0.

The difference between newObject and AllocObject

(1) AllocObject This is to allocate memory space for the object, and there is no variable initialization and construction method call.

newObject initializes variables and calls the specified construction method

(2) Obtain method signature using command Javap

例如:javap -classpath F:\JNI\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes -s com.example.jni.CTransferJava

(3) GetMethodID to obtain the constructor using <init> instead

Knowledge supplement link: https://www.freesion.com/article/3964761069/

Guess you like

Origin blog.csdn.net/wzhrsh/article/details/113932878