前言
我们之前的例子都是没有从Java代码中传参数给C++函数的,但是实际使用中大都是需要进行不同类型的数据传参与获取的,这就涉及到Java和C++的类型对应转换。主要涉及以下几点:
- java方法里面将参数传入本地方法;
- 在本地方法里面创建java对象;
- 在本地方法里面return结果给java程序。
数据类型对应
从Java程序中传到本地方法中的原始类型可以直接使用,对应关系表如下所示:
Java 类型 | 本地类型 | 描述 |
---|---|---|
boolean | jboolean | C/C++8位整型 |
byte | jbyte | C/C++带符号的8位整型 |
char | jchar | C/C++无符号的16位整型 |
short | jshort | C/C++带符号的16位整型 |
int | jint | C/C++带符号的32位整型 |
long | jlong | C/C++带符号的64位整型e |
float | jfloat | C/C++32位浮点型 |
double | jdouble | C/C++64位浮点型 |
void | void | N/A |
Object | jobject | 任何Java对象,或者没有对应java类型的对象 |
Class | jclass | Class对象 |
String | jstring | 字符串对象 |
Object[] | jobjectArray | 任何对象的数组 |
boolean[] | jbooleanArray | 布尔型数组 |
byte[] | jbyteArray | 比特型数组 |
char[] | jcharArray | 字符型数组 |
short[] | jshortArray | 短整型数组 |
int[] | jintArray | 整型数组 |
long[] | jlongArray | 长整型数组 |
float[] | jfloatArray | 浮点型数组 |
double[] | jdoubleArray | 双浮点型数组 |
JNI字符串函数
常用的JNI函数将在后续介绍,这里给出其中的字符串操作函数的函数名以及相关描述。
JNI函数 | 描述 |
---|---|
GetStringChars ReleaseStringChars | 获得/释放一个Unicode格式的字符串指针,可能返回一个字符串的副本 |
GetStringUTFChars ReleaseStringUTFChars | 获得/释放一个UTF-8格式的字符串指针,可能返回一个字符串的副本 |
GetStringLength | 返回Unicode格式字符串的长度 |
GetStringUTFLength | 返回UTF-8格式字符串的长度 |
NewString | 根据Unicode格式的C字符串创建一个Java字符串 |
NewStringUTF | 根据UTF-8格式的C字符串创建一个Java字符串 |
GetStringCritical ReleaseStringCritical | 获得/释放一个Unicode格式的字符串指针,可能返回一个字符串的副本【在该函数对区间内,不能使用任何JNI函数】 |
GetStringRegion | 将Unicode格式的String复制到预分配的缓冲区中 |
GetStringUTFRegion | 将UTF-8格式的String复制到预分配的缓冲区中 |
类型签名
类型签名 | Java 类型 |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
V | void |
L | fully-qualified-class ; 全限定的类 |
[ type | type[] |
( arg-types ) ret-type | 方法类型 |
L
签名较为特殊,在类的全称前面加L
,后面还要加上;
。写参数类型签名时,类型之间没有空格没有,
分隔符。
例如,Java方法:
boolean func(int n, String s, int[] arr);
具有以下类型签名:
(ILjava/lang/String;[I)Z
简单实例
我们用一个简单的动态注册JNI的例子来演示参数传递与字符串处理,从java函数中传递两个int型数据给C++函数,进行加法计算,然后在字符串中返回计算结果。
public class JNIDynamicUtils{
/**
* 调用C++代码的方法,计算两数相加的结果返回对应的字符串
* @return
*/
public static native String getSumFromJNI(int i1, int i2);
/**
* 加载so库或jni库
*/
static {
System.loadLibrary("JNI_DYNAMIC_ANDROID_TEST");
}
}
#include <stdio.h>
#include <jni.h>
#include <stdlib.h>
jstring calcSum(JNIEnv *env, jclass clazz, jint i1, jint i2) {
char result[50];
sprintf(result, " this is string from jni. result is %d", (i1 + i2));
return env->NewStringUTF(result);
}
/**
* JNINativeMethod由三部分组成:
* (1)Java中的函数名;
* (2)函数签名,格式为(输入参数类型)返回值类型;
* ()Ljava/lang/String; (II)表示需要传两个int型参数,Ljava/lang/String;表示返回String,在对象类名(包括包名,‘/’间隔)前面加L,分号结尾
* (3)native函数名
*/
static JNINativeMethod gMethods[] = { {"getSumFromJNI", "(II)Ljava/lang/String;", (void *)calcSum }};
//System.loadLibrary过程中会自动调用JNI_OnLoad,在此进行动态注册
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = NULL;
jint result = JNI_FALSE;
//获取env指针
if (jvm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
return result;
}
if (env == NULL) {
return result;
}
//获取类引用
jclass clazz = env->FindClass("com/xiaoyu/android/tools/JNIDynamicUtils");
if (clazz == NULL) {
return result;
}
//注册方法
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
return result;
}
//成功
result = JNI_VERSION_1_6;
return result;
}
int sprintf( char *buffer, const char *format, [ argument] … );
类似于printf,根据格式化字符串format,将后续参数列表中的参数逐个输出。不过输出目标不是标准输出终端,而是字符串buffer。
点击按钮时调用以下方法(tips:本打算用静态注册jni,后来为了用到类型签名,换成了动态注册。请忽略下面的string from static jni
的错误。):
tvInfo.setText("--->string from static jni: " + JNIDynamicUtils.getSumFromJNI(3, 4));