人间观察
1024-程序员节
愿各位程序员历尽千帆,归来还是少年。
html
这片文章原本不打算写的,由于在前面的文章多多少少的提到了jni和java的交互,可是为了让知识体系更健全写,仍是梳理下,算是jni和java的在交互上的一个总结吧。
二者的交互概括起来主要就是两种。
java
- java调用jni。好比:传递基本数据,复杂对象等
- 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<传参类型>,好比调用AppInfo
的getVersionCode
对应的就是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 为数据类型,好比GetIntField
,GetShortField
等等,若是不是基本型则为GetObjectField
,若是属性为static
的则加上Static
,好比GetStaticIntField
,GetStaticObjectField
。在jni中是不当作员变量的做用域的,无论你是private
,protected
,public
的,加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层
咱们在jni中建立一个java对象,其实就是调用java的构造方法,只不过是构造方法的函数签名为固定值<init>
. 在jni中建立复杂对象(任何java对象)用NewObject方法,一样有不一样参数的NewObjectV
,NewObjectA
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中的方法同样,它也有相似SetLongField
,SetIntField
,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}