JNI訪問靜態or非靜態方法or構造方法
讓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
如何不拷貝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測試亂碼