让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
获取签名
如果出现找不到类
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测试乱码