• JNI 自定义类型参数转换


      在使用java的jni调用C++接口时候, 我们会先把数据转换成基本类型, 比如int, float, double, int[]等等, 一方面减低参数类型转换上的繁琐, 另一方面也许也能减少耦合. 实际应用时候, 可能会遇到希望一个jni接口返回多个参数的情况, 这情况下, 貌似就不得不用自定义类型参数的转换了. 这篇文章, 介绍的就是一个自定义类型参数转换的android例子程序.

      这个例子是二维数组自定义类的, 其实三维四维甚至更多纬的数组的使用方法都是如此类推的.遇到这种需求时候,就能轻松加愉快的解决了.

      关于android jni ndk编程入门, 可以参考这篇文章.

    1. 在java代码中加入一个返回自定义类型的native接口:

    package com.jnitest;
    
    import android.app.Activity;
    import android.os.Bundle;
    
    public class JnitestActivity extends Activity {
        /** Called when the activity is first created. */
    	
    	static
    	{
    		System.loadLibrary("test-jni");
    	}
    	
    	native static PointF[][] createPointFs(int len1, int len2);  //返回PointF的数组,PointF是一个自定义的类
    	
        @Override
        public void onCreate(Bundle savedInstanceState) 
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            PointF[][] points = createPointFs(2, 2);
            for (int i = 0; i < points.length; i++)
            {
            	for (int j = 0; j < points[0].length; j++)
            	{
            		System.out.println(points[i][j].x + "," + points[i][j].y);
            	}
            }
        }
    }
    

      

    下面是PointF.java

    package com.jnitest;

    public class PointF
    {
    public float x;
    public float y;

    public PointF(float xx, float yy)
    {
    x = xx;
    y = yy;
    }
    }

    2. 使用javah命令生成C/C++的头文件.(这篇文章有介绍如何使用javah命令生成头文件)

     生成下图红色框框内的头文件

    3. 实现.cpp文件,代码如下:

    #include "com_jnitest_JnitestActivity.h"
    #include <stdio.h>

    JNIEXPORT jobjectArray JNICALL Java_com_jnitest_JnitestActivity_createPointFs
    (JNIEnv *jenv, jclass jcls, jint jlen1, jint jlen2)
    {
    //convert parameter to C/C++ type
    int len1 = (int) jlen1;
    int len2 = (int) jlen2;

    //create java type PointF
    jclass objectClass = (jenv)->FindClass("com/jnitest/PointF");
    jobjectArray jpointfs1 = (jenv)->NewObjectArray((jsize) len2, objectClass, NULL);
    jobjectArray pointfArrayArray = (jenv)->NewObjectArray((jsize) len1, (jenv)->GetObjectClass(jpointfs1), NULL);
    jmethodID cid = (jenv)->GetMethodID(objectClass, "<init>", "(FF)V");

    for (int j = 0; j < len1; j++)
    {
    jobjectArray jpointfarray = (jenv)->NewObjectArray((jsize) len2, objectClass, NULL);
    for (int i = 0; i < len2; i++)
    {
    jfloat jx = (jfloat) i;
    jfloat jy = (jfloat) j;
    jobject pointF = (jenv)->NewObject(objectClass, cid, jx, jy);
    (jenv)->SetObjectArrayElement(jpointfarray, i, pointF);
    (jenv)->DeleteLocalRef(pointF);
    }
    (jenv)->SetObjectArrayElement(pointfArrayArray, j, jpointfarray);
    (jenv)->DeleteLocalRef(jpointfarray);
    }
    (jenv)->DeleteLocalRef(jpointfs1);

    return pointfArrayArray;
    }


    下面一点一点的解释上面这些代码:

    1) int len1 = (int) jlen1;  

        把java的int类型转换成,C/C++的int类型,直接转换就可以了.

    2) jclass objectClass = (jenv)->FindClass("com/jnitest/PointF");

        获得java自定义类PointF, FindClass的参数,指明的是从source file开始的PointF类的路径

    3) jobjectArray jpointfs1 = (jenv)->NewObjectArray((jsize) len2, objectClass, NULL);

        创建个PointF的数组(参数objectClass指明了这个数组的元素类型), 数组的长度为len2

    4) jobjectArray pointfArrayArray = (jenv)->NewObjectArray((jsize) len1, (jenv)->GetObjectClass(jpointfs1), NULL);

        这里是创建一个以PointF的数组为元素的数组, 实际上它是一个PointF的二维数组, 因为上面定义的java native函数返回的就是一个PointF[][].

    5) jmethodID cid = (jenv)->GetMethodID(objectClass, "<init>", "(FF)V");

        获得PointF构造函数的ID.

        "<init>",这里指明的是函数名字,构造函数就写"<init>".

        "(FF)V",圆括号内表示参数类型,FF代表,有两个参数都是float类型.圆括号后的V,代表函数返回类型是void.关于函数签名, 这里这里都有比较详细的对照表和说明.

    6) jobject pointF = (jenv)->NewObject(objectClass, cid, jx, jy);

        创建一个PointF类型的对象. objectClass是要创建的对象的类型, cid是构造函数的ID, jx和jy是构造函数的两个参数.

    7) (jenv)->SetObjectArrayElement(jpointfarray, i, pointF);

        这行代码就容易理解, 往jpointfarray数组给元素赋值, i 是要赋值元素的下标, pointF就是要赋的值.

    8) (jenv)->DeleteLocalRef(pointF);

        删除引用计数. 这里为什么要删除引用技术呢, 因为(6)里面每次都会new一个对象, 引用计数会加1, 当引用技术超过某个数(好像是500,具体忘记了)就会crash. 这篇文章介绍了LocalRef这个问题.

    4. 编译so文件,并运行程序,查看logcat中的输出如下.

        可以看到创建了一个2*2的二位数组

  • 相关阅读:
    未能加载文件或程序集"xxx"或它的一个依赖项。试图加载格式不正确的程序。
    js实现拖动div,兼容IE、FireFox,暂不兼容Chrome
    WPF 像CSS一样使用 Font Awesome 图标字体
    面向接口、面向对象、面向方面的区别
    ionic 编译 gradle下载。
    ionic 不是外部命令 不是内部命令
    Ionic 编译找不到index.html
    分区表
    安装.net framwork3.5
    MSDTC不可用解决办法
  • 原文地址:https://www.cnblogs.com/wingyip/p/2322789.html
Copyright © 2020-2023  润新知