上一篇我们学习了《JNI数据类型和属性方法》,了解了JNI和Java数据类型的关系,以及JNI访问JAVA类的一些方法,包括静态方法,接下来我们继续学习。
访问构造方法
JAVA类定义native方法
public class Test {
public native Data accessConstructor();
public static void main(String[] args) {
// TODO Auto-generated method stub
Test t = new Test();
Data mData = t.accessConstructor();
DateFormat dateFormat = new SimpleDateFormat("EE MM-dd-yyyy");
System.out.println(dateFormat.format(mData));
System.out.println( mData.toString());
}
//加载动态库
static {
System.loadLibrary("ConsoleApplication1");
}
}
按照一般的步骤,需要生成com.lwj.test.Test 类的头文件com_lwj_test_Test.h
这里演示的是:使用java.util.Date产生一个当前时间时间戳,并且将Date对象返回给Java类的accessConstructor方法。
//实现方法
JNIEXPORT jobject JNICALL Java_com_lwj_test_Test_accessConstructor
(JNIEnv *env, jobject jobj){
//jclass
jclass cls = (*env)->FindClass(env, "java/util/Date");
//构造方法jmethodID
jmethodID mid = (*env)->GetMethodID(env,cls,"<init>","()V");
//实例化一个Date对象
jobject obj = (*env)->NewObject(env,cls,mid);
//调用getTime方法
jmethodID t_mid = (*env)->GetMethodID(env,cls,"getTime","()J");
jlong time = (*env)->CallLongMethod(env,obj,t_mid);
printf("time long:%lld",time);
//jlong -> 字符串
FILE *fp = fopen("D://log.txt", "w");
char str[50];
sprintf(str, "%lld", time);//打印到字符串中
fputs(str, fp);//将字符串写入文件
fclose(fp);
return obj;
}
生成动态库(.dll)文件,之后在Java中调用得到结果
调用父类的方法
Java类:
public class Animal {
public void walk() {
System.out.println("动物都会走路~");
}
}
public class Monkey extends Animal{
@Override
public void walk() {
// TODO Auto-generated method stub
// super.walk();
System.out.println("猴子会爬树~");
}
}
public class Test {
private Animal mMonkey = new Monkey();
public native void callSuperMethod();
public static void main(String[] args) {
// TODO Auto-generated method stub
Test t = new Test();
t.callSuperMethod();
}
//加载动态库
static {
System.loadLibrary("ConsoleApplication1");
}
}
JNI调用
JNIEXPORT void JNICALL Java_com_lwj_test_Test_callSuperMethod
(JNIEnv *env, jobject jobj){
jclass jcls = (*env)->GetObjectClass(env,jobj);
jfieldID fid = (*env)->GetFieldID(env,jcls,"mMonkey"," Lcom/lwj/test/Animal;");
jobject super_obj = (*env)->GetObjectField(env, jobj, fid);
//执行walk方法
jclass super_cls = (*env)->FindClass(env,"com/lwj/test/Animal");//注意:传父类的类名
jmethodID mid = (*env)->GetMethodID(env, super_cls, "walk", "()V");
//执行子类覆盖的方法
//(*env)->CallObjectMethod(env, super_obj, mid);
//执行父类的方法
(*env)->CallNonvirtualVoidMethod(env, super_obj, super_cls, mid);
}
解决中文字符串乱码
C字符串->JSTRING
一般的从JNI返回中文字符串会出现乱码的情况
public class Test {
public native String chineseChars(String text);
public static void main(String[] args) {
// TODO Auto-generated method stub
Test t = new Test();
System.out.println(t.chineseChars("是时候了"));
}
//加载动态库
static {
System.loadLibrary("ConsoleApplication1");
}
}
编写native方法
JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_chineseChars
(JNIEnv *env, jobject jobj, jstring jstr){
char *cstr = "我叫小明";
return (*env)->NewStringUTF(env,cstr);
}
以上的JNI实现就是直接返回一个jstring,出现了乱码。
针对这个情况,在实现native函数中进行处理
JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_chineseChars
(JNIEnv *env, jobject jobj, jstring jstr){
char *cstr = "我叫小明";
//String类的jclass
jclass str_cls = (*env)->FindClass(env, "java/lang/String");
jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");
//char * -> char[] ->jbyteArray -> jbyte *
jbyteArray bytes = (*env)->NewByteArray(env, strlen(cstr));
//bytes数组赋值
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(cstr), cstr);
//charsetName="GB2312"
jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
//返回GB2312中文编码jstring
return (*env)->NewObject(env, str_cls, constructor_mid, bytes, charsetName);
}
这里借用Java的转码API,返回GB2312中文编码字符串,使用java.lang.string的构造函数public String(byte bytes[], String charsetName)完成转码。
执行这个构造方法需要三个东西 1))jmethodID 2)byte数组(jbyteArray)参数 3)charsetName参数(jstring)
最后运行的结果如下:
数组处理
演示:传入int数组,对数组进行排序
Java类
public class Test {
public native void giveArray(int[] array);
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] array = {89,100,34,2,10,5};
Test t = new Test();
t.giveArray(array);
for (int i : array) {
System.out.println(i);
}
}
//加载动态库
static {
System.loadLibrary("ConsoleApplication1");
}
}
实现native方法
int compare(int *a, int *b){
return (*b) - (*a);
}
JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_giveArray
(JNIEnv * env, jobject obj, jintArray arr){
//Java的int数组(jintArray)->C int数组
int *elems = (*env)->GetIntArrayElements(env,arr,NULL);
//数组的长度
int len = (*env)->GetArrayLength(env,arr);
//对(jint)long数组进行
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);
}
说明:对于java传值到Jni实现函数
1、如果是java的基本数据类型,传过来的就是值
2、如果是java的引用类型(对象),传的就是引用
在这里 jintArray 是引用类型,是指向Java数组的指针。
最后需要使用ReleaseIntArrayElements 对 arr 和 elems 进行同步。
返回指定大小的数组
public class Test {
public native int[] getArray(int size);
public static void main(String[] args) {
// TODO Auto-generated method stub
Test t = new Test();
int[] array = t.getArray(10);
for (int i : array) {
System.out.println(i);
}
}
//加载动态库
static {
System.loadLibrary("ConsoleApplication1");
}
}
实现native方法
JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_getArray
(JNIEnv * env, jobject obj, jint size){
jintArray arrs = (*env)->NewIntArray(env,size);
//赋值
//jintArray -> jint *
jint *elems = (*env)->GetIntArrayElements(env,arrs,NULL);
int i = 0;
//循环赋值
for (; i < size; i++){
elems[i] = i;
}
//同步
(*env)->ReleaseIntArrayElements(env, arrs, elems, 0);
return arrs;
}