Android-JNI development series "six" the interaction between jni and java

Human Observation
1024-Programmer's Day
May all programmers have gone through a thousand sails, and they are still young when they return.

This article was originally not intended to be written, because the interaction between jni and java was more or less mentioned in the previous article, but in order to make the knowledge system more sound, I should sort it out. It can be regarded as a summary of the interaction between jni and java. Right.
The interaction between the two can be summarized into two main types.

  1. java calls jni. For example: transfer basic data, complex objects, etc.
  2. jni calls java. Such as callbacks, exceptions, calling java methods/member variables, constructing java objects, etc.

Java calls jni-pass to complex objects to jni

We create a new java object, and then pass it to jni, and get the attribute value of the object in jni.

The java objects are as follows

package com.bj.gxz.jniapp.methodfield;

import java.io.Serializable;

/**
 * Created by guxiuzhong on 2020/10/24.
 */
public class AppInfo implements Serializable {
    private static final String TAG = "AppInfo";
    private String versionName;
    public int versionCode;
    public long size;

    public AppInfo(String versionName) {
        this.versionName = versionName;
    }

    public AppInfo(String versionName, int versionCode) {
        this.versionName = versionName;
        this.versionCode = versionCode;
    }

    public String getVersionName() {
        return versionName;
    }

    public void setVersionName(String versionName) {
        this.versionName = versionName;
    }

    public int getVersionCode() {
        return versionCode;
    }

    public void setVersionCode(int versionCode) {
        this.versionCode = versionCode;
    }

    public void setSize(long size) {
        this.size = size;
    }

    public long getSize() {
        return size;
    }

    @Override
    public String toString() {
        return "AppInfo{" +
                "versionName='" + versionName + '\'' +
                ", versionCode=" + versionCode +
                ", size=" + size +
                '}';
    }
}

The jni interface is

public native void getAppInfoFromJava(AppInfo appInfo);

The method corresponding to jni is

extern "C" JNIEXPORT void  JNICALL
Java_com_bj_gxz_jniapp_methodfield_JNIMethodField_getAppInfoFromJava(JNIEnv *env, jobject instance,
                                                                     jobject obj) {
                 // ...                                                    
}

The last parameter obj corresponds to the object passed by java AppInfo. Because all java objects in jni are jobject. Correspondence, post it here:

Basic data types :

The mapping relationship between java and Native is shown in the following table:

Java type Native type Description
boolean jboolean unsigned 8 bits
byte jbyte signed 8 bits
char jchar unsigned 16 bits
short jshort signed 16 bits
int jint signed 32 bits
long jlong signed 64 bits
float jfloat 32 bits
double jdouble 64 bits
void void not applicable

Reference data type

The outside is in jni, and the java in parentheses.

  • jobject
    • jclass (java.lang.Class objects)
    • jstring (java.lang.String objects)
    • jarray (arrays)
      • jobjectArray (object arrays)
      • jbooleanArray (boolean arrays)
      • jbyteArray (byte arrays)
      • jcharArray (char arrays)
      • jshortArray (short arrays)
      • jintArray (int arrays)
      • jlongArray (long arrays)
      • jfloatArray (float arrays)
      • jdoubleArray (double arrays)
  • jthrowable (java.lang.Throwable objects)

The reference type of jni in the above hierarchy represents the inheritance relationship, jbooleanArray inherits jarray, jarray inherits jobject, and ultimately all inherit jobject.

ok。

jni calls the method of java object

An object method calls Call <return type> Method <parameter passing type>, such as calling AppInfothe getVersionCodecorresponding to that CallIntMethodcall setVersionCodeis the corresponding CallVoidMethodmethod,

Call<return type>Method<parameter passing type> Native type java type
CallVoidMethod() CallVoidMethodA() CallVoidMethodV() void void
CallObjectMethod () CallObjectMethodA () CallObjectMethodV () jobject object
CallBooleanMethod () CallBooleanMethodA () CallBooleanMethodV () jboolean boolean
CallByteMethod() CallByteMethodA() CallByteMethodV() jbyte byte
CallCharMethod() CallCharMethodA() CallCharMethodV() jchar char
CallShortMethod() CallShortMethodA() CallShortMethodV() jshort short
CallIntMethod() CallIntMethodA() CallIntMethodV() jit int
CallLongMethod() CallLongMethodA() CallLongMethodV() jlong long
CallFloatMethod() CallFloatMethod A() CallFloatMethodV() jlong long
CallDoubleMethod () CallDoubleMethodA () CallDoubleMethodV () jfloat float
If the java method is static as follows - -
CallStaticShortMethod() CallStaticShortMethodA() CallStaticShortMethodV() jshort short
Omit other methods... - -

For details, please refer to the official website: official website development documents

Each return type corresponds to 3 methods, the only difference is that the input form of the input parameter is different. So the getVersionNamecorresponding is CallObjectMethod.

CallObjectMethod parameters:
obj: a Java object instance
methodID: the ID of the specified method
args: input parameter list, if the method has no parameters, do not write

Pay special attention
if you are calling a method of a Java object, the CallxxxxMethodfirst parameter is a Java object instance. But if you are calling a static method, the first parameter is jclass. Static methods do not belong to an object.

The methodID is obtained through GetMethodID, jni.hand the function declaration prototype in the header file is:

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

GetMethodID parameter
clazz: Corresponding to the class of java; For the full class name of the java class, for example, change the dot to/that is, com.bj.gxz.jniapp.methodfield.AppInfoto com/bj/gxz/jniapp/methodfield/AppInfo
const char* name method name;
const char* sig method signature, method signature can be obtained through javap -s xxx.class.

getVersionNameThe complete code obtained by the example is as follows:

    // 根据java对象获取对象对应的class
    jclass cls = env->GetObjectClass(obj);
    // 获取&调用java方法
    jmethodID getVersionName_mid = env->GetMethodID(cls, "getVersionName", "()Ljava/lang/String;");
    jstring versionName = (jstring) env->CallObjectMethod(obj, getVersionName_mid);
        char *c_string = const_cast<char *>(env->GetStringUTFChars(versionName, 0));
    LOG_D("versionName=%s", c_string);

Looks very simple.

jni gets the property value of a java object

The method of obtaining the java object corresponding to the attribute value is GetXXXXField, XXXX data type, such as GetIntField, GetShortFieldetc., if was not basic GetObjectField, if the property is staticof the add Static, for example GetStaticIntField, GetStaticObjectField. In jni is do not look at the scope of the member variables, whether you are private, protected, public, and plus finnal, too, it can read the value inside, and the reflection is not the same.

GetXXXXField parameter
obj: a Java object instance
jfieldID: the ID of the specified attribute

GetFieldID parameter
clazz: class
const char* name of the object java object : attribute name
const char* sig: data type descriptor

The corresponding relationship between the data type descriptor java and jni is as follows:

Java type Type descriptor
int I
long J
byte B
short S
char C
float F
double D
boolean WITH
void V
Array [
Two-dimensional array [[
Other reference types L+ full name+;

Examples are as follows:

    // 获取java对象的属性值
    jfieldID versionCode_fieldId = env->GetFieldID(cls, "versionCode", "I");
    int versionCode = env->GetIntField(obj, versionCode_fieldId);
    LOG_D("versionCode=%d", versionCode);

    // 获取java对象的属性值
    jfieldID size_fieldId = env->GetFieldID(cls, "size", "J");
    long size = env->GetLongField(obj, size_fieldId);
    LOG_D("size=%ld", size);

    // 获取java静态属性的值
    jfieldID tag_fieldId = env->GetStaticFieldID(cls, "TAG", "Ljava/lang/String;");
    jstring tag_java = (jstring) env->GetStaticObjectField(cls, tag_fieldId);
    char *tag_c_string = const_cast<char *>(env->GetStringUTFChars(tag_java, 0));
    LOG_D("TAG=%s", tag_c_string);

After running:

        JNIMethodField jniMethodField = new JNIMethodField();

        AppInfo javaInfo = new AppInfo("com.wg.com", 30);
        javaInfo.setSize(500);
        jniMethodField.getAppInfoFromJava(javaInfo);
10-23 23:25:27.572 2900-2900/com.bj.gxz.jniapp D/JNI: versionName=com.wg.com
10-23 23:25:27.572 2900-2900/com.bj.gxz.jniapp D/JNI: versionCode=30
10-23 23:25:27.572 2900-2900/com.bj.gxz.jniapp D/JNI: size=500
10-23 23:25:27.572 2900-2900/com.bj.gxz.jniapp D/JNI: TAG=AppInfo

The acquisition of properties is similar to the acquisition of methods.

Construct java object & call java method in jni

关于的回调异常可以参考前面的文章。

Android-JNI开发系列《二》-在jni层的线程中回调到java层

Android-JNI开发系列《三》-异常处理

我们在jni中创建一个java对象,其实就是调用java的构造方法,只不过是构造方法的函数签名为固定值<init>. 在jni中创建复杂对象(任何java对象)用NewObject方法,同样有不同参数的NewObjectVNewObjectA

jni.h函数声明原型为:

 jobject NewObject(jclass clazz, jmethodID methodID, ...)

参数
clazz java类的class;
methodID 指定方法的ID;
… 参数 支持多个参数;

clazz和methodID同上文方法中的介绍。

示例如下:

    // 获取java的class
    jclass cls = env->FindClass("com/bj/gxz/jniapp/methodfield/AppInfo");

    // 创建java对象,就是调用构造方法,构造方法的方法签名固定为<init>
    jmethodID mid = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
    jobject obj = env->NewObject(cls, mid, env->NewStringUTF("com.gxz.com"));

    // 给定方法名字和签名,调用方法
    jmethodID setVersionCode_mid = env->GetMethodID(cls, "setVersionCode", "(I)V");
    env->CallVoidMethod(obj, setVersionCode_mid, 1);

    // 给定属性名字和签名,设置属性的值
    jfieldID size_field_id = env->GetFieldID(cls, "size", "J");
    env->SetLongField(obj, size_field_id, (jlong) 1000);

size的属性我们是通过SetLongField设置的,见名知义。这个和获取属性一样/调用java中的方法一样,它也有类似SetLongFieldSetIntField,SetStaticIntField,SetObjectField等等,区分了基本类型,静态属性,引用类型。大家可以尝试一下,这里不多介绍了。
运行后:

        AppInfo info = jniMethodField.createAppInfoFromJni();
        Log.e(TAG, "info=" + info);
        

输出

        10-23 23:25:27.572 2900-2900/com.bj.gxz.jniapp E/JNI: info=AppInfo{versionName='com.gxz.com', versionCode=1, size=1000}

最后源代码:https://github.com/ta893115871/JNIAPP

Guess you like

Origin blog.csdn.net/ta893115871/article/details/109259341