jni中的基本类型、字符串和数组

native方法参数

JNIEnv包含jni函数表

Java与Jni之间类型的映射

Java中主要包含两种类型: 基本类型:int, char, boolean等; 引用类型:类, 实例,数组;
注: 不管是基本类型数组还是引用数组, 都是作为引用存在,jni中有对应的接口取到对应的每个元素。

表1. 基本类型数据映射

JavaJni描述
booleanjboolean
bytejbyte
charjchar
shortjshort
intjint
floatjfloat
longjlong
doublejdouble

基本数据类型都是可以在Native层直接使用的

表2. 引用数据类型映射

JavaJni描述
Objectjobject任何Java对象,或者没有对应java类型到对象
ClassjclassClass对象
Stringjstring字符串对象
Object[]jobjectArray任何对象数组
boolean[]jbooleanArray布尔型数组
byte[]jbyteArray比特型数组
char[]jcharArray字符型数组
short[]jshortArray短整型数组
int[]jintArray整型数组
long[]jlongArray长整型数组
float[]jfloatArray浮点型数组
double[]jdoubleArray双浮点型数组
voidvoidn/a

1.引用数据类型则不能直接使用,需要根据JNI函数进行相应的转换后,才能使用;
2.多维数组(包括二维数组)都是引用类型,需要使用 jobjectArray 类型存取其值 ;

相比基本类型,对象类型的传递要复杂很多。 Java 层对象作为 opaque references(指针)传递到 JNI 层。 Opaque references 是一种 C 的指针类型,它指向 JavaVM 内部数据结构。使用这种指针的目的是:不希望 JNI 用户了解 JavaVM 内部数据结构。对 Opaque reference所指结构的操作,都要通过 JNI 方法进行.

类描述符

类描述符也叫类签名。签名的作用是准确到描述一件事物。java vm 定义了类签名和方法签名。 其中,方法签名是为了支持方法重载。
类描述符是类的完整名称(包名+类名),将原来的 . 分隔符换成 / 分隔符。例如:在java代码中的java.lang.String类的类描述符就java/lang/String。 也可以写成:

数组类型的描述符则为,则为: [ + 其类型的域描述符
例如:
int [ ] 其描述符为[I
float [ ] 其描述符为[F
String [ ] 其描述符为[Ljava/lang/String;

域描述符

  1. 基本类型的域描述符定义如下:
    域描述符
  2. 引用类型的域描述符定义如下:
    一般引用类型则为 L + 该类型类描述符 + ; (注意,这儿的分号“;”只得是JNI的一部分,而不是我们汉语中的分段,下同)
    例如:String类型的域描述符为 Ljava/lang/String;
    对于数组,其为 : [ + 其类型的域描述符 + ;
    int[ ] 其描述符为 [I
    float[ ] 其描述符为 [F
    String[ ] 其描述符为 [Ljava/lang/String;
    Object[ ]类型的域描述符为 [Ljava/lang/Object;
    多维数组则是 n个[ +该类型的域描述符 , N代表的是几维数组。例如:
    int [ ][ ] 其描述符为[[I
    float[ ][ ] 其描述符为[[F

方法描述符

将参数类型的域描述符按照申明顺序放入一堆括号中跟返回值类型的域描述符, 规则如下: (参数的域描述符的叠加)返回类型描述符。 对于没有返回值的, 用V(表示void型)。
例如,

java层方法jni 函数签名
String test()Ljava/lang/String;
int f(int i, Object object)(ILjava/lang/Object;)I
void set(byte[] bytes)([B)V

在编程时,如果是利用javah工具进行转换的话,这些都不需要我们手动编写对应到类型转换,如果不是的话,就只能手动进行类型转换了。

访问字符串

Prompt.java

class Prompt {
    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. setProperty ( "java.library.path", "." ) ;
        System.loadLibrary("Prompt");
    }
}

javac Prompt.java
javah -jni Prompt

Prompt.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Prompt */

#ifndef _Included_Prompt
#define _Included_Prompt
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Prompt
 * Method:    getLine
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_Prompt_getLine
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

Prompt.c

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

JNIEXPORT jstring JNICALL Java_Prompt_getLine
  (JNIEnv * env, jobject obj, jstring prompt){
    char buf[128];
    const jbyte *str;
    //调用 GetStringUTFChars,把一个 Unicode 字串转成 UTF-8 格式字串
    str = (*env)->GetStringUTFChars(env, prompt, NULL);
    //
    if (str == NULL)
    {
        return NULL;
    }
    printf("%s", str);
    (*env)->ReleaseStringUTFChars(env, prompt, str);
    scanf("%127s", buf);
    //构造String, 并返回
    return (*env)->NewStringUTF(env, buf);
}

运行结果

wlyuan@wlyuan:~/Project/AndroidWork/Prompt/src$ java -Djava.library.path=. Prompt
Type a line: wlyuan
User typed: wlyuan

注意:
1. 记得检测 GetStringUTFChars 的返回值,因为调用该函数会有内存分配操作,失败后,该函数返回 NULL,并抛 OutOfMemoryError 异常。
2. 调用ReleaseStringUTFCharshna函数释放内存;

Get/RleaseStringCritical方法

      为了尽可能避免内存分配,返回指向 java.lang.String 内容的指针。 Java 2 SDK release 1.2 提供了:Get/RleaseStringCritical。
当使用这对函数时,这对函数间的代码应被当做临界区。在该代码区,不要调用任何会阻塞当前线程和分配对象的 JNI 函数,如 IO 之类的操作 这个原则可以避免死锁到发生: 因为在执行 Get/ReleaseStringCritical 区的代码时,GC 被禁用了,如果因某些原因在其他线程中引发了 JavaVM 执行 GC 操作,VM 有死锁的危险:当前线程 A 进入 Get/RelaseStringCritical 区,禁用了 GC,如果其他线程 B 中有 GC请求,因 A 线程禁用了 GC,所以 B 线程被阻塞了;而此时,如果 B 线程被阻塞时已经获得了一个 A 线程执行后续工作时需要的锁;死锁发生了。

  1. 可以嵌套调用 GetStringCritical。
  2. 2.

访问基本类型数组

IntArray.java

public class IntArray {

    /**
     * @param args
     */
    public static void main(String[] args) {
        IntArray intArr = new IntArray();
        int[] arr = new int[10];
        for (int i = 0; i < 10; i++) {
            arr[i] = i;
        }

        int sum  = intArr.sumArray(arr);
        System.out.println("sum of intArr[10] is " + sum);
    }

    private native int sumArray(int[] arr);

    static {
        System. setProperty ( "java.library.path", "." ) ;
        System.loadLibrary("IntArray");
    }

}

IntArray.c

#include <stdio.h>
#include "IntArray.h"

JNIEXPORT jint JNICALL Java_IntArray_sumArray
  (JNIEnv *env, jobject obj, jintArray arr) {
    jint buf[10];
    jint i, sum = 0;
    sum = 0;
    (*env)->GetIntArrayRegion(env, arr, 0, 10, buf);
    for (i = 0; i < 10; i++) {
        sum += buf[i];
    }
    return sum;
}

访问对象数组

对于对象数组的访问,使用 Get/SetObjectArrayElement,对象数组只提供针对数组的每个元素的 Get/Set,不提供类似 Region 的区域性操作。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值