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.
- java calls jni. For example: transfer basic data, complex objects, etc.
- 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 AppInfo
the getVersionCode
corresponding to that CallIntMethod
call setVersionCode
is the corresponding CallVoidMethod
method,
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 getVersionName
corresponding 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, theCallxxxxMethod
first parameter is a Java object instance. But if you are calling a static method, the first parameter isjclass
. Static methods do not belong to an object.
The methodID is obtained through GetMethodID
, jni.h
and 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.AppInfo
tocom/bj/gxz/jniapp/methodfield/AppInfo
const char* name method name;
const char* sig method signature, method signature can be obtained throughjavap -s xxx.class
.
getVersionName
The 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
, GetShortField
etc., if was not basic GetObjectField
, if the property is static
of 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层
我们在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}