Android JNI 传递对象

听闻少年二字,当与平庸相斥。这篇文章主要讲述Android JNI 传递对象相关的知识,希望能为你提供帮助。
JNI初步入门后,在传递数据的时候,遇到一个需求:有多个数据需要在Java与C代码之间进行传递。如果都做为函数参数传入,则函数很长很难看,并且多个数据的返回也不好实现。所以想到了把数据打包后传递。这在C语言中就是结构体,在java中就是类了。
我们要做的工作就是,先确定要传递的数据,然后相应在C与Java中定义相应的数据类型,然后通过JNI进行数据对应。下面以一个例程来逐步说明。
为了更好的说明各种数据类型的转换示例,我们的数据包含整型、字符串、浮点数、字符、布尔值、数组。
在Java端定义类:

public class ParamInfo { public boolean boolValue; public char charValue; public double doubleValue; public int intValue; public byte[] array; public String str; }

在C端定义结构体:
typedef struct{ bool boolValue; char charValue; double doubleValue; int intValue; char array[255]; char str[255]; }ParamInfo;

jni接口中并不要求两边的变量名一致,或者类名与结构体名一致,只是我们为了逻辑清晰,最好将名称定义的一致,以便于在后续编写代码的过程中更好的将相应数据一一对应起来。
在C代码中获取Java代码传递的参数:
  • 以获取类中一个整型值为例:
    //获取Java中的实例类ParamInfo
jclass jcInfo = env-> FindClass("com/example/helloworld/ParamInfo");

  • 1
【Android JNI 传递对象】其中,com/example/helloworld 是包名对应路径,ParamInfo是包含数据接口的类名。
  • 获取类中一个整型变量intValue的定义
jfieldID jfi = env-> GetFieldID(jcInfo, "intValue", "I");

  • 1
  • 获取实例的变量intValue的值,其中jobj即参数中携带数据的对象:
paramInfo.intValue = https://www.songbingjia.com/android/env-> GetIntField(jobj, jfi);

  • 1
在C代码中设置向Java端传递的参数:以传递结构体中一个整型值为例:
  • 先设置结构体中整型值:
paramInfo.intValue = https://www.songbingjia.com/android/8;

  • 1
  • 获取Java中的实例类ParamInfo
jclass jcInfo = env-> FindClass("com/example/helloworld/ParamInfo");

  • 1
其中com/example/helloworld 是包名对应路径,ParamInfo是包含数据接口的类名。
  • 获取类中一个整型变量intValue的定义
jfieldID jfi = env-> GetFieldID(jcInfo, "intValue", "I");

  • 1
  • 创建新的对象
jobject joInfo = env-> AllocObject(jcInfo);

  • 1
  • 设置实例的变量intValue的值
env-> SetIntField(joInfo, jfi, paramInfo.intValue);

  • 1
  • 最后返回该对象
return joInfo;

  • 1
其余数据类型值的访问,都是类似的步骤。注意 GetFieldID()的第3个参数的取值,是数据类型的签名标识,具体取值见下面表格:
请查看下表:
Java类型符号
boolean Z
byte B
char C
short S
int I
long L
float F
double D
void V
object对象 LClassName; L类名;
Arrays [array-type [数组类型
methods方法 (argument-types)return-type (参数类型)返回类型
native代码知道这些了,就可以进行我们native代码的书写了,如下:
// Java 类向C结构体类型转换 JNIEXPORT jint JNICALL Java_com_example_helloworld_JniClient_setInfo (JNIEnv *env, jobject jo, jobject jobj) { ParamInfo paramInfo; //获取Java中的实例类ParamInfo jclass jcInfo = env-> FindClass("com/example/helloworld/ParamInfo"); //获取类中每一个变量的定义 //boolean boolValue jfieldID jfb = env-> GetFieldID(jcInfo, "boolValue", "Z"); //char charValue jfieldID jfc = env-> GetFieldID(jcInfo, "charValue", "C"); //double charValue jfieldID jfd = env-> GetFieldID(jcInfo, "doubleValue", "D"); //int intValue jfieldID jfi = env-> GetFieldID(jcInfo, "intValue", "I"); //byte[] array jfieldID jfa = env-> GetFieldID(jcInfo, "array", "[B"); //String str jfieldID jfs = env-> GetFieldID(jcInfo, "str", "Ljava/lang/String; "); //获取实例的变量boolValue的值 paramInfo.boolValue = https://www.songbingjia.com/android/env-> GetBooleanField(jobj, jfb); //获取实例的变量charValue的值 paramInfo.charValue = env-> GetCharField(jobj, jfc); //获取实例的变量doubleValue的值 paramInfo.doubleValue = env-> GetDoubleField(jobj, jfd); //获取实例的变量intValue的值 paramInfo.intValue = env-> GetIntField(jobj, jfi); //获取实例的变量array的值 jbyteArray ja = (jbyteArray)env-> GetObjectField(jobj, jfa); intnArrLen = env-> GetArrayLength(ja); char *chArr = (char*)env-> GetByteArrayElements(ja, 0); memcpy(paramInfo.array, chArr, nArrLen); //获取实例的变量str的值 jstring jstr = (jstring)env-> GetObjectField(jobj, jfs); const char* pszStr = (char*)env-> GetStringUTFChars(jstr, 0); strcpy(paramInfo.str, pszStr); //日志输出 LOGI("paramInfo.array=%s, paramInfo.boolValue=https://www.songbingjia.com/android/%d, paramInfo.charValue=%c/n", paramInfo.array, paramInfo.boolValue, paramInfo.charValue); LOGI("paramInfo.doubleValue=https://www.songbingjia.com/android/%lf, paramInfo.intValue=%d,paramInfo.str=%s/n", paramInfo.doubleValue, paramInfo.intValue, paramInfo.str); return 0; }// C结构体类型向Java 类转换 JNIEXPORT jobject JNICALL Java_com_example_helloworld_JniClient_getInfo (JNIEnv *env, jobject jo) { char chTmp[] = "Test array"; int nTmpLen = strlen(chTmp); //将C结构体转换成Java类 ParamInfo paramInfo; memset(paramInfo.array, 0, sizeof(paramInfo.array)); memcpy(paramInfo.array, chTmp, strlen(chTmp)); paramInfo.boolValue = https://www.songbingjia.com/android/true; paramInfo.charValue = ‘B‘; paramInfo.doubleValue = 2.7182; paramInfo.intValue = 8; strcpy(paramInfo.str,"Hello from JNI"); LOGI("paramInfo.array=%s, paramInfo.boolValue=https://www.songbingjia.com/android/%d, paramInfo.charValue=%c/n", paramInfo.array, paramInfo.boolValue, paramInfo.charValue); //获取Java中的实例类 jclass jcInfo = env-> FindClass("com/example/helloworld/ParamInfo"); //获取类中每一个变量的定义 //boolean boolValue jfieldID jfb = env-> GetFieldID(jcInfo, "boolValue", "Z"); //char charValue jfieldID jfc = env-> GetFieldID(jcInfo, "charValue", "C"); //double doubleValue jfieldID jfd = env-> GetFieldID(jcInfo, "doubleValue", "D"); //int intValue jfieldID jfi = env-> GetFieldID(jcInfo, "intValue", "I"); //byte[] array jfieldID jfa = env-> GetFieldID(jcInfo, "array", "[B"); //String str jfieldID jfs = env-> GetFieldID(jcInfo, "str", "Ljava/lang/String; "); //创建新的对象 jobject joInfo = env-> AllocObject(jcInfo); //给类成员赋值 env-> SetBooleanField(joInfo, jfb, paramInfo.boolValue); env-> SetCharField(joInfo, jfc, (jchar)paramInfo.charValue); env-> SetDoubleField(joInfo, jfd, paramInfo.doubleValue); env-> SetIntField(joInfo, jfi, paramInfo.intValue); //数组赋值 jbyteArray jarr = env-> NewByteArray(nTmpLen); jbyte *jby = env-> GetByteArrayElements(jarr, 0); memcpy(jby, paramInfo.array, nTmpLen); env-> SetByteArrayRegion(jarr, 0, nTmpLen, jby); env-> SetObjectField(joInfo, jfa, jarr); //字符串赋值 jstring jstrTmp = env-> NewStringUTF(paramInfo.str); env-> SetObjectField(joInfo, jfs, jstrTmp); return joInfo; }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
Java端测试代码:
// 动态加载C库 System.loadLibrary("HelloWorld"); //进行对象的jni传递 ParamInfo paramInfoSet = new ParamInfo(); byte[] b = new byte[10]; for (int i = 0; i < 9; i++) { b[i] = (byte) (i + 97); } paramInfoSet.array = b; paramInfoSet.boolValue = https://www.songbingjia.com/android/false; paramInfoSet.charValue = ‘C‘; paramInfoSet.doubleValue = 3.14; paramInfoSet.intValue = 2016; paramInfoSet.str ="Hello from Java"; Log.i("Hello", "log: to access lib"); JniClient.setInfo(paramInfoSet); Log.i("Hello", "log: after setInfo"); //进行对象的jni接收 ParamInfo paramInfoGet = JniClient.getInfo(); Log.i("Hello", "log: paramInfoGet.boolValue="https://www.songbingjia.com/android/+ paramInfoGet.boolValue +" paramInfoGet.charValue="https://www.songbingjia.com/android/+ paramInfoGet.charValue +" paramInfoGet.doubleValue="https://www.songbingjia.com/android/+ paramInfoGet.doubleValue); Log.i("Hello", "log: paramInfoGet.intValue="https://www.songbingjia.com/android/+ paramInfoGet.intValue +" paramInfoGet.array=" + new String(paramInfoGet.array) + " paramInfoGet.str=" + paramInfoGet.str); //将收到的字符串显示到界面上 TextView tv_say_hello = (TextView) findViewById(R.id.tv_say_hello); tv_say_hello.setText(paramInfoGet.str); Log.i("Hello", "log: finish");

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
最后输出的日志信息:
06-25 17:04:25.740: I/Hello(19039): log: to access lib 06-25 17:04:25.740: I/logfromc(19039): paramInfo.array=abcdefghi, paramInfo.boolValue=https://www.songbingjia.com/android/0, paramInfo.charValue=C 06-25 17:04:25.740: I/logfromc(19039): paramInfo.doubleValue=3.140000, paramInfo.intValue=2016,paramInfo.str=Hello from Java 06-25 17:04:25.740: I/Hello(19039): log: after setInfo 06-25 17:04:25.740: I/logfromc(19039): paramInfo.array=Test array, paramInfo.boolValue=1, paramInfo.charValue=B 06-25 17:04:25.740: I/Hello(19039): log: paramInfoGet.boolValue=true paramInfoGet.charValue=B paramInfoGet.doubleValue=2.7182 06-25 17:04:25.740: I/Hello(19039): log: paramInfoGet.intValue=8 paramInfoGet.array=Test array paramInfoGet.str=Hello from JNI 06-25 17:04:25.740: I/Hello(19039): log: finish

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
可以看出,java设置的数值,已经成功传递到C端;而C程序设置的数据,也成功回传到java端了。
demo下载:http://download.csdn.net/detail/lintax/9559413



    推荐阅读