native方法参数

JNIEnv包含jni函数表

Java与Jni之间类型的映射

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

表1. 基本类型数据映射

Java

Jni

描述

boolean

jboolean

byte

jbyte

char

jchar

short

jshort

int

jint

float

jfloat

long

jlong

double

jdouble

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

表2. 引用数据类型映射

Java

Jni

描述

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

双浮点型数组

void

void

n/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 之类的操作

  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 的区域性操作。