java jni 修改ip_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命令生成头文件)

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

8285def4e02ab5800890b913353c5001.png

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

#include "com_jnitest_JnitestActivity.h"

#include

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, "", "(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, "", "(FF)V");

获得PointF构造函数的ID.

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

"(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的二位数组

0294e98e9b53891eb109405f14e77896.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值