快速入门
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) 简单数据类型
Java | JNI | C |
---|---|---|
int | jint | int |
double | jdouble | double |
float | jfloat | float |
boolean | jboolean | unsigned char 有两个常量:JNI_TRUE 和JNI_FALSE 分别对应1和0 |
String | jstring | char* |
除了几个基本类型外,所有的类类型在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类型 | 符号 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | L |
float | F |
double | D |
void | V |
objects对象 | Lfully-qualified-class-name; 注意此处要有分号 |
Arrays数组 | [array-type |
methods方法 | (argument-types)return-type |
GetIntField
第一个参数是jobject
,即需要处理的对象,第二个是jfieldID
。返回int
同样有GetDoubleField
、GetObjectField
。
III. 改变成员变量
有如下方式:
env->SetIntField(obj, aFid, 30);
同样有SetDoubleField
、SetObjectField
。
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) 数组类型:
Java | JNI | C |
---|---|---|
int[] | jintArray | jint* |
double[] | jdoubleArray | jdouble* |
float[] | jfloatArray | jfloat* |
String[] | jobjectArray | String等其他类类型的数组以及二维数组都对应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);
同理有GetDoubleArrayElements
、ReleaseDoubleArrayElements
。
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);
}