JNI快速入门手册

4 篇文章 0 订阅
2 篇文章 0 订阅

快速入门

1. 设置环境变量

  • windows下配置以下环境变量,以及JAVA的环境变量(不在此列出)
PATH                C:\MinGW\bin; 
LIBRARY_PATH        C:\MinGW\lib 
C_INCLUDEDE_PATH    C:\MinGW\include 
CPLUS_INCLUDE_PATH  C:\MinGW\include\c++\3.4.2;C:\MinGW\include\c++\3.4.2\mingw32;C:\MinGW\include\c++\3.4.2\backward;C:\MinGW\include
  • Linux下配置以下环境变量,要么配置到/etc/profile 里要么配置到用户目录的.bash_profile里
export JAVA_HOME=/usr/lib/jvm/java-6-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH

2. 编写Test.java

class Test{
    {
        System.loadLibrary("test");
    }
    public native void display();
    public native double sum(double x, double y);
    public static void main(String[] args){
        Test a = new Test();
        System.out.println(a.sum(3,5));
        a.display();
    }
}

3. 由Test.java自动生成Test.h

$javah Test 

注意不用在Test后跟上.java,否则会出错。
有可能报错:

error: cannot access Test
class file for Test not found
javadoc: error - Class Test not found.
Error: No classes were specified on the command line.  Try -help.

此时需要先运行第6步,生成Test.class后才能用javah生成Test.h

4. 编写test.c

#include "Test.h"
#include "stdio.h"

/*
 * Class:     Test
 * Method:    display
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Test_display
  (JNIEnv *env, jobject obj)
{
    printf("hello world!\n");
}

/*
 * Class:     Test
 * Method:    sum
 * Signature: (DD)D
 */
JNIEXPORT jdouble JNICALL Java_Test_sum
  (JNIEnv * env, jobject obj, jdouble a, jdouble b)
{
    return a+b;
}

5. 编译成链接库

  • windows:
>gcc -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -Wl,-kill-at -s -o test.dll test.c
  • linux:
$gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared -o libTest.so test.c

linux下可编写Makefile如下,可以实现C和C++的混编

CC = gcc
CXXFLAGS = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -fPIC -shared -lstdc++
LIBJNI_SO = libTest.so
SRC_FILE = $(wildcard *.c *.cpp)

$(LIBJNI_SO) : $(SRC_FILE)
        $(CC) $(CXXFLAGS) -o $@ $^

clean:
        rm *.so

6. 编译java

$javac Test.java

7. 运行

$java Test

8. 注意点

如果系统是64位的,java也是64位的,需要装64位的gcc。
http://sourceforge.net/projects/mingw-w64/
装完要重新配置环境变量

JNI讲解

0. 简介

JNIEnv *env代表的是当前的JNI环境,各种函数都要从这里调用。所有Java的本地函数对应的JNI函数,第一个参数都是JNIEnv *env,第二个参数是当前对象或者类。

1. C和C++区别

两者调用env的各种函数名都一样,但是C里的仅是个函数指针,比C++里多传一个参数就是env自己。
比如:

  • C里:
jclass (JNICALL *FindClass)(JNIEnv *env, const char *name);`
  • 对应C++里:
jclass FindClass(const char *name);`

调用的时候:

  • C里:
JNIEnv *env;
(*env)->FindClass(env, "Result");
  • C++里:
 JNIEnv *env;
 env->FindClass("Result");

其他函数类似。

2. 函数名对应:

Java里函数对应的JNI函数名为Java_包名_类名_函数名_参数区分,最后面的参数区分只有在多个函数有相同函数名时才会出现。

3. 数据类型对应:

1) 简单数据类型

JavaJNIC
intjintint
doublejdoubledouble
floatjfloatfloat
booleanjbooleanunsigned char 有两个常量:JNI_TRUEJNI_FALSE分别对应1和0
Stringjstringchar*

除了几个基本类型外,所有的类类型在JNI里对应的都是jobject类型。

2) String类型

jstring要转化成char*需要调用GetStringUTFChars
如:

jstring js;
const char* str = env->GetStringUTFChars(js, 0);

记得在使用完以后,调用ReleaseStringUTFChars来释放内存

env->ReleaseStringUTFChars(js, str);

char*要转化成jstring需要调用NewStringUTF函数:

js = env->NewStringUTF(str);

3) 类类型

类类型的对象一般都会变成jobject
比如:
成员函数第二个参数都是jobject,代表当前函数所在的对象:

JNIEXPORT jfloat JNICALL Java_Test_sum___3F(JNIEnv *env, jobject obj, jfloatArray arr)

除非是静态函数,第二个参数是jclass:

JNIEXPORT jobject JNICALL Java_Test_getDate(JNIEnv *env, jclass cls)
I. 获取一个类类型

可以通过FindClass或者GetObjectClass来获取,前者需要传进去一个字符串, 注明包名和类名,后者需要传一个jobject:

jclass dateClass = env->FindClass("java/util/Date");
jobject obj;
jclass curClass = env->GetObjectClass(obj);
II. 获取对象的成员变量

有如下方式:

jfieldID aFid = env->GetFieldID(curClass, "a", "I");
int num = (int)env->GetIntField(obj, aFid);

GetFieldID第一个参数是jclass类型,第二个是变量名,第三个是变量类型的对应符,有以下几种:

Java类型符号
booleanZ
byteB
charC
shortS
intI
longL
floatF
doubleD
voidV
objects对象Lfully-qualified-class-name; 注意此处要有分号
Arrays数组[array-type
methods方法(argument-types)return-type

GetIntField第一个参数是jobject,即需要处理的对象,第二个是jfieldID。返回int
同样有GetDoubleFieldGetObjectField

III. 改变成员变量

有如下方式:

env->SetIntField(obj, aFid, 30);

同样有SetDoubleFieldSetObjectField

IV. 调用对象的成员方法

有如下方式:

jmethodID getYearID = env->GetMethodID(dateClass, "getYear", "()I");
jint year = env->CallIntMethod(date, getYearID);

GetMethodID第一个参数是jclass类型,第二个是函数名,第三个是参数类型及返回值,参数类型写在括号里,返回值写在括号后,比如说如果函数参数类型有(int a, float b, String c, double d),返回值是void,则
写成"(IFLjava/lang/String;D)V",注意objects对象对应的地方那个分号。
CallIntMethod第一个参数是jobject,第二个是jmethodID,后面可以跟对应参数。

V. 新建一个对象

需要如下调用:

jint sum = 0;
jstring arg = env->NewStringUTF("all");
jclass resultClass = env->FindClass("Result");
jmethodID resID = env->GetMethodID(resultClass, "<init>", "(ILjava/lang/String;)V");
jobject res = env->NewObject(resultClass, resID, sum ,arg);

NewObject第一个参数是jclass,第二个是jmethodID,后面跟对应参数。

VI. 调用类的静态方法

需要如下调用:

jmethodID printMid = env->GetStaticMethodID(curClass, "print", "()V");
env->CallStaticVoidMethod(curClass, printMid);

4) 数组类型:

JavaJNIC
int[]jintArrayjint*
double[]jdoubleArrayjdouble*
float[]jfloatArrayjfloat*
String[]jobjectArrayString等其他类类型的数组以及二维数组都对应jobjectArray
I. 普通类型数组

数组长度通过GetArrayLength获取,内容需要通过GetXXXArrayElements获取
如下:

jintArray arr;
jsize len = env->GetArrayLength(arr);
jint *body = env->GetIntArrayElements(arr, 0);
for(int i = 0 ; i < len ; ++i)
{
    printf("%d\n", body[i]);
    body[i] = i;
}

记得在使用完后调用ReleaseXXXArrayElements来写回数据并释放内存

env->ReleaseIntArrayElements(arr, body, 0);

同理有GetDoubleArrayElementsReleaseDoubleArrayElements

II. 创建一个jintArray

可以通过NewIntArray来创建,参数传递创建的长度

jintArray newArray = env->NewIntArray(len);

设置内容:

jint tmp[len];
env->SetIntArrayRegion(newArray, 0, len, tmp);
III. 类类型数组

对应jobjectArray,通过GetArrayLength获取数组长度,通过GetObjectArrayElement来获取数组某个元素。如下:

jobjectArray result;
jsize len = env->GetArrayLength(result);
for(int i = 0 ; i < len ; ++i)
{
    jobject obj = env->GetObjectArrayElement(result, i);
}

SetObjectArrayElement来对数组进行修改。

IV. 二维数组

对应jobjectArray,获取方式和类类型数组相同,只不过获取到的每个元素可以强转成jarray,如下:

jobjectArray arr;
jsize len = env->GetArrayLength(arr);
int sum = 0;
for(int i = 0 ; i < len ; ++i)
{
    jarray body = (jarray)env->GetObjectArrayElement(arr, i);
    jsize len2 = env->GetArrayLength(body);
    jint *body2 = env->GetIntArrayElements((jintArray)body, 0);
    for(int j = 0 ; j < len2 ; ++j)
    {
        sum += body2[j];
    }
    env->ReleaseIntArrayElements((jintArray)body, body2, 0);
}

示例代码下载

下载地址:http://download.csdn.net/detail/a578559967/8545449

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值