Android-JNI开发系列《六》jni与java的交互

人间观察
1024-程序员节
愿各位程序员历尽千帆,归来还是少年。

html

这片文章原本不打算写的,由于在前面的文章多多少少的提到了jni和java的交互,可是为了让知识体系更健全写,仍是梳理下,算是jni和java的在交互上的一个总结吧。
二者的交互概括起来主要就是两种。
java

  1. java调用jni。好比:传递基本数据,复杂对象等
  2. jni调用java。好比回调,异常,调用java方法/成员变量,构造java对象等等

java调用jni-传到复杂对象到jni中

咱们新建一个java的对象,而后传递到jni中,在jni中获取该对象的属性值。git

java对象以下程序员

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 +
                '}';
    }
}

jni接口为github

public native void getAppInfoFromJava(AppInfo appInfo);

对应jni的方法是数组

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

最后一个参数obj就是对应java传过来的对象AppInfo。 由于在jni中全部的java对象都是jobject。对应关系,这里再贴一下:oracle

基本数据类型app

java与Native映射关系以下表所示:ide

Java类型 Native 类型 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

引用数据类型函数

外面的为jni中的,括号中的java中的。

  • 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)

上面的层次中的jni的引用类型表明了继承关系,jbooleanArray继承jarray,jarray继承jobject,最终都继承jobject。

ok。

jni调用java对象的方法

调用对象的某个方法 Call<返回类型>Method<传参类型>,好比调用AppInfogetVersionCode对应的就是CallIntMethod,调用setVersionCode对应的就是CallVoidMethod方法,

Call<返回类型>Method<传参类型> Native 类型 java类型
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() jint int
CallLongMethod() CallLongMethodA() CallLongMethodV() jlong long
CallFloatMethod() CallFloatMethod A() CallFloatMethodV() jlong long
CallDoubleMethod() CallDoubleMethodA() CallDoubleMethodV() jfloat float
若是java方法是静态的以下 - -
CallStaticShortMethod() CallStaticShortMethodA() CallStaticShortMethodV() jshort short
省略其它方法… - -

具体能够参考官网:官网开发文档

每一种返回类型对应3个方法,惟一不一样的是输入参数的传入形式不一样。因此getVersionName对应的就是CallObjectMethod

CallObjectMethod参数:
obj:某个 Java 对象实例
methodID:指定方法的ID
args:输入参数列表,方法若是没有参数则不写


特别注意
若是你调用的是Java对象的方法CallxxxxMethod第一个参数是某个 Java 对象实例。可是若是你调用的是静态方法,则第一个参数是jclass。静态方法并不属于某一个对象。

methodID的获取经过GetMethodID,在jni.h头文件中函数声明原型为:

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

GetMethodID参数
clazz: 对应java的class类; 为java类的全类名好比把点改成/即com.bj.gxz.jniapp.methodfield.AppInfo改成 com/bj/gxz/jniapp/methodfield/AppInfo
const char* name 方法名字;
const char* sig 方法的签名,方法的签名能够经过javap -s xxx.class获取。


示例获取getVersionName的完整代码以下:

// 根据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);

看着很简单吧。

jni获取java对象的属性值

获取java对象的属性对应的值的方法为GetXXXXField,XXXX 为数据类型,好比GetIntFieldGetShortField等等,若是不是基本型则为GetObjectField,若是属性为static的则加上Static,好比GetStaticIntFieldGetStaticObjectField。在jni中是不当作员变量的做用域的,无论你是privateprotectedpublic的,加finnal也同样,它均可以读取里面的值,和反射不同。

GetXXXXField参数
obj:某个 Java 对象实例
jfieldID:指定属性的ID

GetFieldID参数
clazz:对象java对象的class
const char* name:属性名字
const char* sig:数据类型描述符


数据类型描述符java和jni的对应关系以下,来:

Java类型 类型描述符
int I
long J
byte B
short S
char C
float F
double D
boolean Z
void V
数组 [
二维数组 [[
其余引用类型 L+类全名+;

例子以下:

// 获取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);

运行后:

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

属性的获取和方法的获取都差很少。

jni中构造java对象&调用java方法

关于的回调异常能够参考前面的文章。

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

相关文章
相关标签/搜索