Android jni/ndk编程二:jni数据类型转换(primitive,String,array)

前一篇文章中我们初步的认识了jni的相关概念,并通过静态和动态的方式体验了一把jni编程。这一片文章将进一步探究jni编程的基本知识,并且将重点集中在java数据类型映射到本地数据类型这一方面。

一.数据类型映射概述

从我们开始jni编程起,就不可能避开函数的参数与返回值的问题。java语言的数据类型和c/c++有很多不同的地方,所以我们必须考虑当在java层调用c/c++函数时,怎么正确的把java的参数传给c/c++函数,怎么正确的从c/c++函数获取正确的函数返回值;反之,当我们在c/c++中使用java的方法或属性时,如何确保数据类型能正确的在java和c/c++之间转换。
回顾我们上一篇文章中的那个c函数:

#include <stdio.h>
#include <jni.h>
#include <stdlib.h>
JNIEXPORT jstring JNICALL Java_com_jinwei_jnitesthello_MainActivity_sayHello(JNIEnv * env, jobject obj){
    return (*env)->NewStringUTF(env,"jni say hello to you");
}

这个函数非常简单,它没有接受参数,但是它返回了一个字符串给java层。我们不能简单的使用return “jni say hello to you”;而是使用了NewStringUTF函数做了个转换,这就是数据类型的映射。
普通的jni函数一般都会有两个参数:JNIEnv * env, jobject obj,第三个参数起才是该函数要接受的参数,所以这里说它没有接受参数。

1.1JNIEnv * env

JNIEnv是一个线程相关的结构体, 该结构体代表了 Java 在本线程的运行环境 。这意味不同的线程各自拥有各一个JNIEnv结构体,且彼此之间互相独立,互不干扰。NIEnv结构包括了JNI函数表,这个函数表中存放了大量的函数指针,每一个函数指针又指向了具体的函数实现,比如,例子中的NewStringUTF函数就是这个函数表中的一员。
JVM,JNIEnv与native function的关系可用如下图来表述:
这里写图片描述

1.2 jobject obj

这个参数的意义取决于该方法是静态还是实例方法(static or an instance method)。
当本地方法作为一个实例方法时,第二个参数相当于对象本身,即this. 当本地方法作为
一个静态方法时,指向所在类. 在本例中,sayHello方法是实例方法,所以obj就相当于this指针。

二.基本数据类型的映射

在Java中有两类数据类型:primitive types,如,int, float, char;另一种为
reference types,如,类,实例,数组。
java基本类型与c/c++基本类型可以直接对应,对应方式由jni规范定义:
这里写图片描述
JNI基本数据类型的定义在jni.h中:

typedef unsigned char   jboolean;       /* unsigned 8 bits */
typedef signed char     jbyte;          /* signed 8 bits */
typedef unsigned short  jchar;          /* unsigned 16 bits */
typedef short           jshort;         /* signed 16 bits */
typedef int             jint;           /* signed 32 bits */
typedef long long       jlong;          /* signed 64 bits */
typedef float           jfloat;         /* 32-bit IEEE 754 */
typedef double          jdouble;        /* 64-bit IEEE 754 */

也就是说jni.h中定义的数据类型已经是c/c++数据类型了,我们使用jint等类型的时候要明白其实使用的就是int 数据类型。

2.1实践

我们在上一篇博客中实现的程序的基础上进一步实验,现在给native_sayHello函数添加一个参数,c代码如下:

#include <stdio.h>
#include <jni.h>
#include <stdlib.h>



jstring native_sayHello(JNIEnv * env, jobject obj,jint num){
    char array[30];
    snprintf(array,30,"jni accept num : %d",num);
    return (*env)->NewStringUTF(env,array);
}

static JNINativeMethod gMethods[] = {  
{
  "sayHello", "(I)Ljava/lang/String;", (void *)native_sayHello},  
};  

JNIEXPORT jint  JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)  
{  
    JNIEnv* env = NULL; //注册时在JNIEnv中实现的,所以必须首先获取它
    jint result = -1;

    if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) //从JavaVM获取JNIEnv,一般使用1.4的版本
      return -1;

    jclass clazz;
    static const char* const kClassName="com/jinwei/jnitesthello/MainActivity";

    clazz = (*env)->FindClass(env, kClassName); //这里可以找到要注册的类,前提是这个类已经加载到java虚拟机中。 这里说明,动态库和有native方法的类之间,没有任何对应关系。

    if(clazz == NULL)
    {
      printf("cannot get class:%s\n", kClassName);
      return -1;
    }

    if((*env)->RegisterNatives(env,clazz,gMethods, sizeof(gMethods)/sizeof(gMethods[0]))!= JNI_OK) //这里就是关键了,把本地函数和一个java类方法关联起来。不管之前是否关联过,一律把之前的替换掉!
    {
      printf("register native method failed!\n");
      return -1;
    } 

    return JNI_VERSION_1_4;  
}

这里还是使用动态的方式注册本地方法,相比较之前的代码,这里只做了两处修改:
1.native_sayHello增加了一个参数jint num;
2.函数签名也随之改变: “(I)Ljava/lang/String;”,之前是 “()Ljava/lang/String;”
android层的代码也随之改变:

    @Overri
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值