Android JNI/NDK入门指南目录
JNI/NDK入门指南之正确姿势了解JNI和NDK
JNI/NDK入门指南之JavaVM和JNIEnv
JNI/NDK入门指南之JNI数据类型,描述符详解
JNI/NDK入门指南之jobject和jclass
JNI/NDK入门指南之javah和javap的使用和集成
JNI/NDK入门指南之Eclipse集成NDK开发环境并使用
JNI/NDK入门指南之JNI动/静态注册全分析
JNI/NDK入门指南之JNI字符串处理
JNI/NDK入门指南之JNI访问数组
JNI/NDK入门指南之C/C++通过JNI访问Java实例属性和类静态属性
JNI/NDK入门指南之C/C++通过JNI访问Java实例方法和类静态方法
JNI/NDK入门指南之JNI异常处理
JNI/NDK入门指南之JNI多线程回调Java方法
JNI/NDK入门指南之正确姿势了解,使用,管理,缓存JNI引用
JNI/NDK入门指南之调用Java构造方法和父类实例方法
JNI/NDK入门指南之C/C++结构体和Java对象转换方式一
JNI/NDK入门指南之C/C++结构体和Java对象转换方式二
在前面的篇章C/C++结构体和Java对象之间通过Jni相互转换方式一中已经介绍了一种方法,可能有些对Android源码或者Jni比较熟悉的读者会说,在源码里面很少会用到前面的方法,而是通过Jni里面的jfieldID和jmethodID直接操作Java对象来进行相转换。下面开启本篇介绍之旅,方式二介绍。
在这里将要使用到是比较传统的方法,借助NDK中jni.h头文件中定义的方法,直接操作通过Jni传递过来的Java层的Object对象,获取object中jfieldID域值然后赋值到C/C++中Struct结构体中这样就完成了Java对象对C/C++结构体的转换。然后对Object中的jfieldID域进行相关的赋值操作,这样就完成了相关的相互的转换。
在前面的章节已经介绍了,具体使用什么方法。那么在本章节里面将介绍具体的步骤,那么,下面来开始我们的介绍。
本篇的标题是C/c++结构体和Java对象之间的转换,那么下面介绍我们要相互转换的数,这里只是为了做演示使用,至于数据还可以进行扩展,或者学完此种方法以后读者也可以亲自操作实践一把。
(1) C/C++结构体
//对应om.xxx.object2struct.JavaBean的数据
typedef struct{
bool boolValue; //boolean
char charValue; //char
double doubleValue; //double
int intValue; //int
char arrayValue[4]; //byte[]
int double_dimen_array[2][2]; // int[][]
const char * stringValue;
const char * message; // String
}JNI_JavaBean;
//对应com.xxx.object2struct.JavaBean$InnerClass的域和方法
typedef struct{
jclass clazz;
jfieldID message;
jmethodID constructor;
}InnerClass_t;
//对应com.xxx.object2struct.JavaBean的域和方法
typedef struct {
jclass clazz;
jfieldID boolValue;
jfieldID charValue;
jfieldID doubleValue;
jfieldID intVaule;
jfieldID byteArray;
jfieldID double_dimen_array;
jfieldID stringValue;
jfieldID inner_message;
jmethodID constructor;
}JavaBean_t;
(2) Java类
package com.xxx.object2struct;
public class JavaBean {
public boolean boolValue = false; // boolean类型
public char charValue = 'A'; // char类型
public double doubleValue = 100; // double类型
public int intValue = 100; // 整形
public byte[] array = {
0, 1, 2, 3}; // byte数组
public int[][] mDoubleDimenArray = {
{
10,10},
{
20,20}
}; // 二维数组
public String stringValue = "Hello!";
public InnerClass mInnerClass = new InnerClass(); // 静态内部类
static class InnerClass {
public String mMessage = "Im from Java!";// 字符串
@Override
public String toString() {
return "InnerClass [mMessage=" + mMessage + "]";
}
}
@Override
public String toString() {
return "JavaBean [boolValue=" + boolValue + ", charValue=" + charValue
+ ", doubleValue=" + doubleValue + ", intValue=" + intValue
+ ", array=" + array.toString() + ", mDoubleDimenArray="
+ mDoubleDimenArray.toString() + ", stringValue=" + stringValue
+ ", mInnerClass=" + mInnerClass + "]";
}
}
前面的章节介绍了,需要转换的数据,那么在这个章节将要介绍具体的步骤。
public class JniTransfer {
// 将C/C++结构体转为Java类
public static native JavaBean getJavaBeanFromNative();
//将Java对象转换成C/C++结构体
public static native void transferJavaBeanToNative(JavaBean javaBean);
// 加载jni库
static {
System.loadLibrary("java2native-transfer");
}
}
这个比较简单,主要就是通过Jni将Java对象传递到Jni层进行相关的转换,然后加以打印。就不加以赘述了,因为确实代码量比较少太简单,我想讲解也没有啥好讲解的。
private void getJavaBeanFromC() {
JavaBean javaBean = JniTransfer.getJavaBeanFromNative();
Log.d("JniTransfer", javaBean.toString());
}
private void javaBeanToStruct() {
JavaBean javaBean = new JavaBean();
JniTransfer.transferJavaBeanToNative(javaBean);
}
这个比前面的第一种方法要复杂一点,因为涉及到本地Native方法的注册,相关类信息的注册。下面我们就一个步骤一个步骤的类详细介绍:
static JNINativeMethod gMethods[] = {
{
"getJavaBeanFromNative", "()Lcom/xxx/object2struct/JavaBean;",(void*)Java_com_xxx_object2struct_JniTransfer_getJavaBeanFromNative },
{
"transferJavaBeanToNative", "(Lcom/xxx/object2struct/JavaBean;)V",(void*)Java_com_xxx_object2struct_JniTransfer_transferJavaBeanToNative },
};
//动态注册Native方法
static int register_native_interface(JNIEnv * env){
jclass clazz;
clazz = env->FindClass(kClassPathName);
if(clazz == NULL)
return JNI_FALSE;
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0){
return JNI_FALSE;
}
return JNI_TRUE;
}
jint JNI_OnLoad(JavaVM * vm, void * reserved){
JNIEnv * env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE(TAG, "ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
if (register_native_interface(env) < 0) {
LOGE(TAG,"ERROR: native registration failed\n");
goto bail;
}
register_classes(env);
result = JNI_VERSION_1_4;
bail:
return result;
}
static JavaBean_t javaBean_t;
static InnerClass_t innerClass_t;
#define ARRAY_LEN(x) ((int) (sizeof(x) / sizeof((x)[0])))
static int dump_JavaBean_Info(JNI_JavaBean * javaBean){
int i =0;
int lenght = 0;
LOGE(TAG, "+++ dump javaBean info start +++\n");
LOGE(TAG, "+++ boolValue : %d +++\n", javaBean->boolValue);
LOGE(TAG, "+++ charValue : %c +++\n", javaBean->charValue);
LOGE(TAG, "+++ doubleVaule : %lf +++\n", javaBean->doubleValue);
LOGE(TAG, "+++ intValue : %d +++\n", javaBean->intValue);
lenght = ARRAY_LEN(javaBean->arrayValue);
for(i = 0; i < lenght; i++)
{
LOGE(TAG, "+++ byte[%d] : %d +++\n", i, javaBean->arrayValue[i]);
}
for(i =0 ; i <2; i++){
for(int j = 0; j <2; j++)
{
LOGE(TAG, "+++ byte[%d] : %d +++\n", i, javaBean->arrayValue[i]);
LOGE(TAG, "+++ double[%d][%d]: %d +++\n", i, j, javaBean->double_dimen_array[i][j]);
}
}
LOGE(TAG, "+++ stringValue : %s +++\n", javaBean->stringValue);
LOGE(TAG, "+++ inner_message : %s +++\n", javaBean->message);
LOGE(TAG, "+++ dump javaBean info end +++\n");
}
static int find_class(JNIEnv * env, const char *className, jclass * classOut)
{
jclass clazz = env->FindClass(className);
if(clazz == NULL)
{
LOGE(TAG,"Can't find %s\n", className);
return -1;
}
*classOut = (jclass) env->NewGlobalRef(clazz); // 这里必须新建一个全局的引用
return 0;
}
static int get_field(JNIEnv * env, jclass clazz, const char *name, const char *sig, jfieldID * field_out)
{
jfieldID filed = env->GetFieldID(clazz, name, sig);
if (filed == NULL) {
LOGE(TAG, "Can't find. filed name: %s, sig: %s", name, sig);
return -1;
}
*field_out = filed;
return 0;
}
static void register_inner_class(JNIEnv * env)
{
int ret = find_class(env, "com/xxx/object2struct/JavaBean$InnerClass", &innerClass_t.clazz);
if(ret != 0)
{
LOGE(TAG,"register_inner_class failed, please check you code!\n");
return;
}
jclass inner_clazz = innerClass_t.clazz;
// 获取构造方法
innerClass_t.constructor = env->GetMethodID(inner_clazz, "" , "()V");
// 获取成员
get_field(env, inner_clazz, "mMessage", "Ljava/lang/String;", &innerClass_t.message);
}
static void register_javaBean_class(JNIEnv * env)
{
int ret = find_class(env, "com/xxx/object2struct/JavaBean", &javaBean_t.clazz);
if(ret != 0)
{
LOGE(TAG,"register_JavaBean_class failed, please check you code!\n");
return;
}
jclass javaBean_clazz = javaBean_t.clazz;
//在Jni中获取构造方法
javaBean_t.constructor = env->GetMethodID(javaBean_clazz, "" , "()V");
//获取成员方法
//Signature: (ZCDI[B[[ILjava/lang/String;)V
get_field(env, javaBean_clazz, "boolValue", "Z", &javaBean_t.boolValue);
get_field(env, javaBean_clazz, "charValue", "C", &javaBean_t.charValue);
get_field(env, javaBean_clazz, "doubleValue", "D", &javaBean_t.doubleValue);
get_field(env, javaBean_clazz, "intValue", "I", &javaBean_t.intVaule);
get_field(env, javaBean_clazz, "array", "[B", &javaBean_t.byteArray);
get_field(env, javaBean_clazz, "mDoubleDimenArray", "[[I", &javaBean_t.double_dimen_array);
get_field(env, javaBean_clazz, "stringValue", "Ljava/lang/String;", &javaBean_t.stringValue);
get_field(env, javaBean_clazz, "mInnerClass", "Lcom/xxx/object2struct/JavaBean$InnerClass;", &javaBean_t.inner_message);
}
static void register_classes(JNIEnv* env)
{
register_javaBean_class(env);
register_inner_class(env);
}
/**
java class use jni transfer to c struct
**/
static void javaBean_java_2_c(JNIEnv *env , jobject javaBean_in, JNI_JavaBean * jni_JavaBean_out)
{
if(javaBean_in == NULL)
{
LOGE(TAG,"javaBean_in is NULL, please check you input or code!\n");
return;
}
LOGE(TAG,"Start javaBean_java_2_c fun\n");
//get boolValue
jni_JavaBean_out->boolValue = env->GetBooleanField(javaBean_in, javaBean_t.boolValue);
//get charValue
jni_JavaBean_out->charValue = env->GetCharField(javaBean_in, javaBean_t.charValue);
//get doubleValue
jni_JavaBean_out->doubleValue = env->GetDoubleField(javaBean_in, javaBean_t.doubleValue);
//get intValue
jni_JavaBean_out->intValue = env->GetIntField(javaBean_in, javaBean_t.intVaule);
//get byte array
jbyteArray byteArray = (jbyteArray)env->GetObjectField(javaBean_in, javaBean_t.byteArray);
jbyte * byte_data = env->GetByteArrayElements(byteArray, 0);
jsize length = env->GetArrayLength(byteArray);
LOGE(TAG,"The byteArray len : %d\n", length);
//copy data
memcpy(jni_JavaBean_out->arrayValue, byte_data, length * sizeof(jbyte));
//get double dimen int array
jobjectArray objectArray = (jobjectArray)env->GetObjectField(javaBean_in, javaBean_t.double_dimen_array);
length = env->GetArrayLength(objectArray);
LOGE(TAG,"doublemin int array len : %d\n", length);
for(int i = 0; i < length; i++)
{
jintArray intArray = (jintArray)env->GetObjectArrayElement(objectArray, i);
jint * sub_int = env->GetIntArrayElements(intArray, 0);
jsize sub_len = env->GetArrayLength(intArray); // 获取列数
LOGE(TAG,"sub_len: %d", sub_len);
memcpy(jni_JavaBean_out->double_dimen_array[i], sub_int, sub_len * sizeof(jint));
env->ReleaseIntArrayElements(intArray, sub_int, 0);
}
//get stringValue
LOGE(TAG,"get StringValue\n");
jstring stringValue = (jstring)env->GetObjectField(javaBean_in, javaBean_t.stringValue);
jni_JavaBean_out->stringValue = env->GetStringUTFChars(stringValue, 0);
//get inner class data
LOGE(TAG,"get inner class data\n");
jobject inner_object = env->GetObjectField(javaBean_in, javaBean_t.inner_message);
jstring message = (jstring)env->GetObjectField(inner_object, innerClass_t.message);
jni_JavaBean_out->message = env->GetStringUTFChars(message, 0);
}
/**
c struct use jni transfer to java class
**/
static jobject javaBean_c_2_java(JNIEnv *env , JNI_JavaBean * jni_JavaBean)
{
if(jni_JavaBean == NULL)
{
LOGE(TAG,"javaBean_c_2_java failed, jni_JavaBean is NULL\n");
return NULL;
}
LOGE(TAG,"javaBean_c_2_java\n");
LOGE(TAG,"innerObject\n");
//1.create InnerClass class
jobject innerObject = env->NewObject(innerClass_t.clazz, innerClass_t.constructor);
jstring message = env->NewStringUTF(jni_JavaBean->message);
env->SetObjectField(innerObject, innerClass_t.message, message);
LOGE(TAG,"javaBeanObject\n");
//2.create JavaBean class
jobject javaBeanObject = env->NewObject(javaBean_t.clazz, javaBean_t.constructor);
//set boolValue
env->SetBooleanField(javaBeanObject, javaBean_t.boolValue, jni_JavaBean->boolValue);
LOGE(TAG,"set charValue\n");
//set charValue
env->SetCharField(javaBeanObject, javaBean_t.charValue, jni_JavaBean->charValue);
LOGE(TAG,"set doubleVaule\n");
//set doubleVaule
env->SetDoubleField(javaBeanObject, javaBean_t.doubleValue, jni_JavaBean->doubleValue);
LOGE(TAG,"set intValue\n");
//set intValue
env->SetIntField(javaBeanObject, javaBean_t.intVaule, jni_JavaBean->intValue);
LOGE(TAG,"set StringValue\n");
//set StringValue
jstring stringValue = env->NewStringUTF(jni_JavaBean->stringValue);
env->SetObjectField(javaBeanObject, javaBean_t.stringValue, stringValue);
LOGE(TAG,"set byte[] array\n");
//set byte[] array
jsize length = ARRAY_LEN(jni_JavaBean->arrayValue);
LOGE(TAG,"The byteArray len : %d\n", length);
jbyteArray byteArray = env->NewByteArray(length);
env->SetByteArrayRegion(byteArray, 0, length, (jbyte *)jni_JavaBean->arrayValue);
env->SetObjectField(javaBeanObject, javaBean_t.byteArray, byteArray);
LOGE(TAG,"set double dimen int array\n");
//set double dimen int array
//设置二维数组的时候需要要一定的技巧性
length = ARRAY_LEN(jni_JavaBean->double_dimen_array);
LOGE(TAG,"The double_dimen_array : %d\n", length);
jclass clazz = env->FindClass("[I");//一维数组的类
jobjectArray double_int_array = env->NewObjectArray(length, clazz, NULL);
for(int i = 0; i < length; i++)
{
jsize sub_len = ARRAY_LEN(jni_JavaBean->double_dimen_array[i]);
LOGE(TAG,"The sub[%d] len: %d\n", i, sub_len);
jintArray intArray = env->NewIntArray(sub_len);
env->SetIntArrayRegion(intArray, 0, sub_len, jni_JavaBean->double_dimen_array[i]);
env->SetObjectArrayElement(double_int_array, i, intArray);
}
env->SetObjectField(javaBeanObject, javaBean_t.double_dimen_array, double_int_array);
//set innerClass
env->SetObjectField(javaBeanObject, javaBean_t.inner_message, innerObject);
return javaBeanObject;
}
/*
* Class: com_xxx_object2struct_JniTransfer
* Method: getJavaBeanFromNative
* Signature: ()Lcom/xxx/object2struct/JavaBean;
*/
JNIEXPORT jobject JNICALL Java_com_xxx_object2struct_JniTransfer_getJavaBeanFromNative
(JNIEnv * env, jclass clazz)
{
JNI_JavaBean javaBean= {
true,
'c',
1,
2,
{
1, 2, 3, 4},
{
{
5,6},
{
7,8}
},
"Im StringValue",
"Im message",
};
dump_JavaBean_Info(&javaBean);
jobject obj = javaBean_c_2_java(env, &javaBean);
return obj;
}
/*
* Class: com_xxx_object2struct_JniTransfer
* Method: transferJavaBeanToNative
* Signature: (Lcom/xxx/object2struct/JavaBean;)V
*/
JNIEXPORT void JNICALL Java_com_xxx_object2struct_JniTransfer_transferJavaBeanToNative
(JNIEnv * env, jclass clazz, jobject object)
{
JNI_JavaBean jni_JavaBean;
javaBean_java_2_c(env, object, &jni_JavaBean);
dump_JavaBean_Info(&jni_JavaBean);
}
在前面的章节里面,我们将原理和步骤都已经交代和讲解OK了,那么到了最终的步骤了就是见证奇迹的时刻到了,看下我们的效果。是不是已经OK了.
λ adb logcat -s JniTransfer
--------- beginning of main
--------- beginning of system
12-02 17:13:39.640 27863 27911 E JniTransfer: getJavaBeanFromC
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ dump javaBean info start +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ boolValue : 1 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ charValue : c +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ doubleVaule : 1.000000 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ intValue : 2 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ byte[0] : 1 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ byte[1] : 2 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ byte[2] : 3 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ byte[3] : 4 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ byte[0] : 1 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ double[0][0]: 5 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ byte[0] : 1 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ double[0][1]: 6 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ byte[1] : 2 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ double[1][0]: 7 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ byte[1] : 2 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ double[1][1]: 8 +++
12-02 17:13:39.647 27863 27911 I JniTransfer: +++ stringValue : Im StringValue +++
12-02 17:13:39.648 27863 27911 I JniTransfer: +++ inner_message : Im message +++
12-02 17:13:39.648 27863 27911 I JniTransfer: +++ dump javaBean info end +++
12-02 17:13:39.648 27863 27911 I JniTransfer: javaBean_c_2_java
12-02 17:13:39.648 27863 27911 I JniTransfer: innerObject
12-02 17:13:39.648 27863 27911 I JniTransfer: javaBeanObject
12-02 17:13:39.648 27863 27911 I JniTransfer: set charValue
12-02 17:13:39.648 27863 27911 I JniTransfer: set doubleVaule
12-02 17:13:39.648 27863 27911 I JniTransfer: set intValue
12-02 17:13:39.648 27863 27911 I JniTransfer: set StringValue
12-02 17:13:39.648 27863 27911 I JniTransfer: set byte[] array
12-02 17:13:39.648 27863 27911 I JniTransfer: The byteArray len : 4
12-02 17:13:39.648 27863 27911 I JniTransfer: set double dimen int array
12-02 17:13:39.648 27863 27911 I JniTransfer: The double_dimen_array : 2
12-02 17:13:39.648 27863 27911 I JniTransfer: The sub[0] len: 2
12-02 17:13:39.648 27863 27911 I JniTransfer: The sub[1] len: 2
12-02 17:13:39.649 27863 27911 D JniTransfer: JavaBean [boolValue=true, charValue=c, doubleValue=1.0, intValue=2, array=[B@ec430b3, mDoubleDimenArray=[[I@dd17670, stringValue=Im StringValue, mInnerClass=InnerClass [mMessage=Im message]]
12-02 17:13:39.649 27863 27911 E JniTransfer:
12-02 17:13:39.649 27863 27911 E JniTransfer: javaBeanToStruct
12-02 17:13:39.649 27863 27911 I JniTransfer: Start javaBean_java_2_c fun
12-02 17:13:39.650 27863 27911 I JniTransfer: The byteArray len : 4
12-02 17:13:39.650 27863 27911 I JniTransfer: doublemin int array len : 2
12-02 17:13:39.650 27863 27911 I JniTransfer: sub_len: 2
12-02 17:13:39.650 27863 27911 I JniTransfer: sub_len: 2
12-02 17:13:39.650 27863 27911 I JniTransfer: get StringValue
12-02 17:13:39.650 27863 27911 I JniTransfer: get inner class data
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ dump javaBean info start +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ boolValue : 0 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ charValue : A +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ doubleVaule : 100.000000 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ intValue : 100 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ byte[0] : 0 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ byte[1] : 1 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ byte[2] : 2 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ byte[3] : 3 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ byte[0] : 0 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ double[0][0]: 10 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ byte[0] : 0 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ double[0][1]: 10 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ byte[1] : 1 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ double[1][0]: 20 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ byte[1] : 1 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ double[1][1]: 20 +++
12-02 17:13:39.650 27863 27911 I JniTransfer: +++ stringValue : Hello! +++
12-02 17:13:39.651 27863 27911 I JniTransfer: +++ inner_message : Im from Java! +++
12-02 17:13:39.651 27863 27911 I JniTransfer: +++ dump javaBean info end +++
如上就将C/C++结构体和Java对象之间通过Jni相互转换的两种方式介绍完了,这两种都是比较常见的方法,读者朋友们掌握了这两种方法以后,再难的转换无外乎也可以使用这两种方式。最后希望本篇能对实际工作中的你有一定的帮助作用。最后附上实例工程C_Java_Transfer.zip。青山不改绿水长流,各位江湖见!