NDK开发 从入门到放弃(三:类型对应、字符串的操作、类型签名)

前言

我们之前的例子都是没有从Java代码中传参数给C++函数的,但是实际使用中大都是需要进行不同类型的数据传参与获取的,这就涉及到Java和C++的类型对应转换。主要涉及以下几点:

  1. java方法里面将参数传入本地方法;
  2. 在本地方法里面创建java对象;
  3. 在本地方法里面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 
#include 
#include 

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));

NDK开发 从入门到放弃(三:类型对应、字符串的操作、类型签名)_第1张图片

你可能感兴趣的:(NDK)