Android的NDK开发JNI传递参数和返回值

本文详细介绍了Java Native Interface (JNI) 中Java与C/C++数据类型之间的映射关系,包括基本数据类型、字符串、数组等的传递方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

我们在使用JNI时最常问到的是JAVA和C / C ++之间如何传递数据,以及数据类型之间如何互相映射。我们从整数等基本类型和数组,字符串等普通的对象类型开始讲述。至于如何传递任意对象,将在后面会更新。

原文链接请标明: 
http://blog.youkuaiyun.com/u011974987/article/details/52743495 
本文其区别是:【stromxu的博客】


正文

JNI流程调用简介及这篇文章,我们再来实现一个非静态的本地方法。

Java的端:

public class JniTest {

    //静态的
    public native static String getStringFromC();

    //非静态的
    public native String getString2FromC(int i);

    public static void main(String[] args) {
        String text = getStringFromC();
        System.out.println(text);

        JniTest t = new JniTest();
        text = t.getString2FromC(6);
        System.out.println(text);

    }

    //加载动态库
    static{ 
        System.loadLibrary("jni_study");
    }

}

 
  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在原生层实现getString2FromC非静态方法;

JNIEXPORT jstring JNICALL Java_com_study_jni_JniTest_getString2FromC
(JNIEnv *env, jobject jobj, jint num){
    return (*env)->NewStringUTF(env,"C String2");
}
 
  • 1
  • 2
  • 3
  • 4

由此看出,每个本机函数,都至少有两个参数(的JNIEnv *,JCLASS或者jobject)

  • 当原生方法为静态方法时: 
    JCLASS代表天然方法所属类的类对象(JniTest.class)
  • 当本地方法为非静态方法时: 
    jobject代表本地方法所属的对象

1.Java基本数据类型传递

用过的Java的人都知道,Java的中的基本类型包括布尔,字节,焦炭,短,整型,长,浮动,双这样几种,如果你用这几种类型做本地方法的参数,当你通过JAVAH -jni生成的.h文件的时候,只要看一下生成的.H文件,就会一清二楚,这些类型分别对应的类型是jboolean,jbyte,jchar,jshort,今题,jlong​​,jfloat,jdouble。这几种类型几乎都可以当成对应的C ++类型来用。

Java的基本数据类型与JNI数据类型的映射关系如下:

JNI的基本数据类型

对应的Java的引用数据类型:

struct _jobject;

typedef struct _jobject *jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
 
  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

关系图:

这里写图片描述

最终都是jobject的结构体指针类型。


2.String参数的传递

Java的字符串的和C ++字符串的是不能对等起来的,所以处理起来比较麻烦。先看一个例子

class Prompt {

// native method that prints a prompt and reads a line
private native String getLine(String prompt);

public static void main(String args[]) {
Prompt p = new Prompt();
String input = p.getLine("Type a line: ");

System.out.println("User typed: " + input);
}

static {
System.loadLibrary("Prompt");
}
}

 
  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这个例子中,我们要实现一个本机方法,函数getline字符串(字符串的提示);读入一个参数字符串,字符串返回一个值通过执行JAVAH -jni得到的头文件是这样的。

#include <jni.h>
#ifndef _Included_Prompt
#define _Included_Prompt
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject this, jstring prompt);
#ifdef __cplusplus
}
#endif
#endif

 
  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

的jstring是JNI中对应于字符串的类型,但是和基本类型不同的是,的jstring不能直接当作C ++串用的。如果你用

cout << prompt << endl;
 
  • 1

器compile-会肯定你扔给一个错误信息的
其实要处理的jstring有很多种方式,只讲这里我一种最认为简单的方式,看下面这个例子:

#include "Prompt.h"
#include <iostream>

JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
const char* str;
str = env->GetStringUTFChars(prompt, false);
if(str == NULL) {
return NULL; 
}

std::cout << str << std::endl;

//释放资源
env->ReleaseStringUTFChars(prompt, str);

// 返回一个字符串
char* tmpstr = "return string succeeded";
jstring rtstr = env->NewStringUTF(tmpstr);
return rtstr;
}

 
  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在上面的列子代码中,作为参数的提示不能直接被C ++程序使用,先做了如下转换

str = env->GetStringUTFChars(prompt, false);
 
  • 1

将的jstring类型变成一个字符*类型。
返回的时候,要生成一个的jstring类型的对象,也必须通过如下方式,

jstring rtstr = env->NewStringUTF(tmpstr);
 
  • 1

这里用到的GetStringUTFCharsNewStringUTF都是JNI提供的处理字符串类型的函数。


3.数组类型的传递

和字符串一样,JNI为爪哇基本类型的数组提供了Ĵ*阵列类型,比如INT []对应的就是jintArray。来看一个传递INT数组的例子。

java的端主要代码:

    public native void giveArray(int[] array);

    int[] array = {9,100,10,37,5,10};
        //排序
    t.giveArray(array);

    for (int i : array) {
        System.out.println(i);
    }
 
  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9

Ç实现主要代码:

int compare(int *a,int *b){
    return (*a) - (*b);
}

//传入
JNIEXPORT void JNICALL Java_com_study_jni_JniTest_giveArray
(JNIEnv *env, jobject jobj, jintArray arr){
    //jintArray -> jint指针 -> c int 数组
    jint *elems = (*env)->GetIntArrayElements(env, arr, NULL);
    //printf("%#x,%#x\n", &elems, &arr);

    //数组的长度
    int len = (*env)->GetArrayLength(env, arr);
    //排序
    qsort(elems, len, sizeof(jint), compare);   

    //同步
    //mode
    //0, Java数组进行更新,并且释放C/C++数组
    //JNI_ABORT, Java数组不进行更新,但是释放C/C++数组
    //JNI_COMMIT,Java数组进行更新,不释放C/C++数组(函数执行完,数组还是会释放)

    (*env)->ReleaseIntArrayElements(env, arr, elems, JNI_COMMIT);
}
 
  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

这个代码中的GetIntArrayElementsReleaseIntArrayElements函数就是JNI提供用于处理INT数组的函数。

如果用ARR [I]的方式去访问jintArray类型,不用问肯定会出错。
JNI还提供了另一对函数GetIntArrayRegionReleaseIntArrayRegion访问INT数组,不在这里做介绍,至于其他的类型数组,方法类似。


4.返回数组

在中JNI,二维数组和字符串数组都被视为对象数组,因为数组和被视为String对象。最后一个示例说明如何在本地代码中创建一个字符串数组并将它返回给Java的调用者。

java的端的代码:

    int[] array2 = t.getArray(10);
    System.out.println("------------");
    for (int i : array2) {
        System.out.println(i);
    }
 
  • 1
  • 2
  • 3
  • 4

函数实现:

//返回数组
JNIEXPORT jintArray JNICALL Java_com_study_jni_JniTest_getArray(JNIEnv *env, jobject jobj, jint len){
    //创建一个指定大小的数组
    jintArray jint_arr = (*env)->NewIntArray(env, len);
    jint *elems = (*env)->GetIntArrayElements(env, jint_arr, NULL); 
    int i = 0;
    for (; i < len; i++){
        elems[i] = i;
    }

    //同步
    (*env)->ReleaseIntArrayElements(env, jint_arr, elems, 0);   

    return jint_arr;
}
 
  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

jintArray jint_arr;。因为要返回值,所以需要创建一个指定大小jintArray对象根据jintArray对象拿到今题指针然后,赋值,同步,通过这样参数报道查看就可以了



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值