JNI访问静态or非静态方法or构造方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liudao7994/article/details/81700553

让eclipse自动加载dll文件.具体操作: 在环境变量里面添加visual studio编译生成的dll文件夹目录.

感觉很少用参数中的Jclass 一般都用

JNIEnv * env, jobject obj

只有用静态本地方法的时候才会出现jClass

例子01 c 调用 非静态 java 方法

java代码;

package test2;

import java.util.Random;

public class JniMethodDemo {
	static {
		System.loadLibrary("myJniOne");
	}
	public native void accessMethod();
	
	int getRandeomInt(int max){
		return new Random().nextInt(max);
	}
	
	public static void main(String[] args) {
		JniMethodDemo jniMethodDemo = new JniMethodDemo();
		jniMethodDemo.accessMethod();
	}
}

c 代码;

#include "stdafx.h"
//stdafx.h 预处理命令
#include "test2_JniMethodDemo.h"
#include <string.h>
#include <Windows.h>

//访问java 非静态方法
JNIEXPORT void JNICALL Java_test2_JniMethodDemo_accessMethod
(JNIEnv* env, jobject jobj){
	//jclass
	jclass jclz = (*env)->GetObjectClass(env, jobj);
	if (NULL == jclz){
		printf("jclz == null");
	}
	//jmethodId
	//方法的名字 方法的签名
	jmethodID mid = (*env)->GetMethodID(env, jclz, "getRandeomInt", "(I)I");
	if (NULL == mid){
		printf("mid == null");
	}
	//调用java的方法
	jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
	if (NULL == random){
		printf("randow == null");
	}
	printf("C random: %d \n", random);
}



打印结果:

C random: 145 

代码解释

具体如何生成.h 头文件 如何生成dll文件 不说了 只说下这里签名的问题

jmethodID mid = (*env)->GetMethodID(env, jclz, "getRandowInt", "(I)I");

这里方法的签名含义: 返回值类型是I 参数是I 如果返回值是空则是();
方法签名获取的方式: cmd 下 到当前java 类目录下 执行javah命令 生成.class文件
我这里是

javac JniMain 

然后cd 到 .class 文件下 执行

javap -s -p JniMethodDemo

获取签名
mark

如果出现找不到类

javap -classpath . -s JniMethodDemo

来自 https://blog.csdn.net/fenzzz/article/details/50109167

如何不拷贝dll文件 自动识别. 在环境变量里面添加c生成dll 的文件夹
用户环境变量->Path->把生成的dll文件夹添加到环境变量里面

例子02 c 调用 静态 java 方法

java

	public native void accessStaticMethod();
	
		static String getRandeomUUId() {
		return UUID.randomUUID().toString();
	}
		public static void main(String[] args) {
		JniMethodDemo jniMethodDemo = new JniMethodDemo();
		// jniMethodDemo.accessMethod();
    	jniMethodDemo.accessStaticMethod();

	}

c:

/*
* Class:     test2_JniMethodDemo JNI 访问Java中的静态方法
* Method:    accessStaticMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_test2_JniMethodDemo_accessStaticMethod
(JNIEnv * env, jobject jobj){
	//jclass
	jclass jclz = (*env)->GetObjectClass(env, jobj);
	//jmethdId,GETStaticMethodID  方法的名字,方法的签名
	jmethodID jmid = (*env)->GetStaticMethodID(env, jclz, "getRandeomUUId", "()Ljava/lang/String;");

	//调用静态发方法
	jstring uuid = (*env)->CallStaticObjectMethod(env, jclz, jmid);

	//jstring -> char *  将jstring转化成 char* 
	char * uuid_c = (*env)->GetStringUTFChars(env, uuid, NULL);

	//下面把用uuid作为文件名
	char filename[100];
	//sprintf指的是字符串格式化命令,主要功能是把格式化的数据写入某个字符串中, 打印给filename字符串
	sprintf(filename, "D://%s.txt", uuid_c);
	FILE * fp = fopen(filename, "w");
	fputs("this is test text", fp);
	fclose(fp);
	printf("write text success\n");

}

在d盘可以看到一个txt文本 内容是this is test text

例子03 c 调用 java 构造方法

java

	public native Date acceessConstructor();
		public static void main(String[] args) {
		JniMethodDemo jniMethodDemo = new JniMethodDemo();
		// jniMethodDemo.accessMethod();
//		jniMethodDemo.accessStaticMethod();
		jniMethodDemo.acceessConstructor();
		
	}

c:

//访问java构造方法
JNIEXPORT jobject JNICALL Java_com_dongnao_alvin_Jni_1Test_acceessConstructor
(JNIEnv * env, jobject jobj) {
	//通过类的路径来从JVM 里面找到对应的类
	jclass jclz = (*env)->FindClass(env, "java/util/Date");
	//jmethodid
	jmethodID jmid = (*env)->GetMethodID(env, jclz, "<init>", "()V");

	//调用 newObject 实例化Date 对象,返回值是一个jobjcct
	jobject date_obj = (*env)->NewObject(env, jclz, jmid);

	//得到对应对象的方法,前提是,我们访问了相关对象的构造函数创建了这个对象
	jmethodID time_mid = (*env)->GetMethodID(env, jclz, "getTime", "()J");
	jlong time = (*env)->CallLongMethod(env, date_obj, time_mid);

	printf("time: %lld \n", time);
	return date_obj;
}

打印结果:

time: 841643519 
 

例子04 解决字符串乱码问题 解决方式1

GetStringChars 的第三个参数 是用来得到是否需要拷贝了,true 就是在内存里面拷贝了一份,开辟了一块内存,需要释放内存.经常和releaseStringUTFChair一起使用; false, 如果没拷贝,那么不用释放内存,没有在内存开辟一块空间,不用管.

为什么乱码:

  • java内部是使用的16bit的unicode编码(utf-16)来表示字符串的,无论英文还是中文都是2字节;
  • jni内部是使用utf-8编码来表示字符串的,utf-8是变长编码的unicode,一般ascii字符是1字节,中文是3字节;
  • c/c++使用的是原始数据,ascii就是一个字节,中文一般是GB2312编码,用2个字节表示一个汉字。

编码格式

在最开始必须确定eclipse 和visual studio 的编码格式是否同意 如果都是都是utf-8或者都是GBK.
vasual studio 设置 文件->高级保存选项->编码-> GBK 或者UTF-8;
eclipse 下设置 window->prefereces-> 环境使用的是utf-8 还是 GBK;
如果是GBK:
使用下面的代码转换:

/*************************************************
*将UTF-8编码的字符串转为GB2312编码
*输入:
*p:指向待转码字符串
*返回:
*指向已转码字符串的指针
*过程:
*将UTF-8转为Unicode编码
*再将Unicode转为GB2312
*************************************************/
char* Utf8ToGb2312(char *p){
	DWORD dwNum = MultiByteToWideChar(CP_UTF8, 0, p, -1, NULL, 0);
	char *psText;
	wchar_t *pwText = (wchar_t*)malloc(dwNum*sizeof(wchar_t));
	dwNum = MultiByteToWideChar(CP_UTF8, 0, p, -1, pwText, dwNum);
	dwNum = WideCharToMultiByte(CP_ACP, 0, pwText, -1, NULL, 0, NULL, NULL);
	psText = (char*)malloc(dwNum*sizeof(char));
	dwNum = WideCharToMultiByte(CP_ACP, 0, pwText, -1, psText, dwNum, NULL, NULL);
	free(pwText);
	return psText;
}

//调用


JNIEXPORT jstring JNICALL Java_test2_JniMethodDemo_chineseChars
(JNIEnv * env, jobject jobj, jstring jstr){
	
	char * cstr = (*env)->GetStringUTFChars(env, jstr, NULL);

	printf("在c中输出传入的字符串 %s Utf8ToGb2312\n", Utf8ToGb2312(cstr));

	return NULL;
}

java 代码:

	public native String chineseChars(String str);

	public static void main(String[] args) {
		JniMethodDemo jniMethodDemo = new JniMethodDemo();
		// jniMethodDemo.accessMethod();
//		jniMethodDemo.accessStaticMethod();
//		jniMethodDemo.acceessConstructor();
		jniMethodDemo.chineseChars("java测试");
	
	}

乱码解决方式2

实际上就是调用java 的方法


JNIEXPORT jstring JNICALL Java_test2_JniMethodDemo_chineseChars
(JNIEnv * env, jobject jobj, jstring jstr){
	
	char *c_str = "c测试乱码";
	jclass str_cls = (*env)->FindClass(env, "java/lang/String");
	jmethodID jmid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");
	
	//jstring -> jbyteArray
	jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
	// 将Char * 赋值到 bytes
	(*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str);
	jstring charsetName = (*env)->NewStringUTF(env, "GB2312");

	return (*env)->NewObject(env, str_cls, jmid, bytes, charsetName);
}

java 调用:

	public static void main(String[] args) {
		JniMethodDemo jniMethodDemo = new JniMethodDemo();
		// jniMethodDemo.accessMethod();
//		jniMethodDemo.accessStaticMethod();
//		jniMethodDemo.acceessConstructor();
		System.out.println(jniMethodDemo.chineseChars("java测试"));
	
	}

打印结果:

c测试乱码

发现一个问题 如果eclipse编码格式是utf-8 都要乱码 很奇怪

猜你喜欢

转载自blog.csdn.net/liudao7994/article/details/81700553