• 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");
    
    
    其中,com/example/helloworld 是包名对应路径,ParamInfo是包含数据接口的类名。
    
        获取类中一个整型变量intValue的定义
    
        jfieldID jfi = env->GetFieldID(jcInfo, "intValue", "I");
    
    
        获取实例的变量intValue的值,其中jobj即参数中携带数据的对象:
    
        paramInfo.intValue = env->GetIntField(jobj, jfi);
    
    
    在C代码中设置向Java端传递的参数:
    
    以传递结构体中一个整型值为例:
    
        先设置结构体中整型值:
    
    paramInfo.intValue = 8;
    
    
        获取Java中的实例类ParamInfo
    
        jclass jcInfo = env->FindClass("com/example/helloworld/ParamInfo");
    
    
    其中com/example/helloworld 是包名对应路径,ParamInfo是包含数据接口的类名。
    
        获取类中一个整型变量intValue的定义
    
       jfieldID jfi = env->GetFieldID(jcInfo, "intValue", "I");
    
        创建新的对象
    
     jobject joInfo = env->AllocObject(jcInfo);
    
        设置实例的变量intValue的值
    
    env->SetIntField(joInfo, jfi, paramInfo.intValue);
    
        最后返回该对象
    
    return joInfo;
    
    其余数据类型值的访问,都是类似的步骤。注意 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 = 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);
        int  nArrLen = 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=%d, paramInfo.charValue=%c
    ",
               paramInfo.array, paramInfo.boolValue, paramInfo.charValue);
        LOGI("paramInfo.doubleValue=%lf, paramInfo.intValue=%d,  paramInfo.str=%s
    ",
              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 = true;
        paramInfo.charValue = 'B';
        paramInfo.doubleValue = 2.7182;
        paramInfo.intValue = 8;
        strcpy(paramInfo.str, "Hello from JNI");
    
        LOGI("paramInfo.array=%s, paramInfo.boolValue=%d, paramInfo.charValue=%c
    ",
               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;
    }
    
    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 = 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=" + 
    
    paramInfoGet.boolValue 
                    + " paramInfoGet.charValue=" + 
    
    paramInfoGet.charValue
                    + " paramInfoGet.doubleValue=" + 
    
    paramInfoGet.doubleValue);
            Log.i("Hello", "log: paramInfoGet.intValue=" + 
    
    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");
    
        最后输出的日志信息:
    
    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=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
    
    可以看出,java设置的数值,已经成功传递到C端;而C程序设置的数据,也成功回传到java端了。
    demo下载:
    
    http://download.csdn.net/detail/lintax/9559413
  • 相关阅读:
    逻辑分析推理(海盗分金问题)
    使用数组构建 ExtJs TreeStore 结构
    逻辑分析推理(找出轻球问题)
    逻辑分析推理(骗子购物问题)
    逻辑分析推理(戴帽子问题)博弈
    简单的排序算法(冒泡、选择、插入)
    逻辑分析推理(五小姐问题)
    逻辑分析推理(倒水问题)
    关于排序(快速排序)
    游戏开发基础(2)
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/6831778.html
Copyright © 2020-2023  润新知