1. 程式人生 > >JNI訪問靜態or非靜態方法or構造方法

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

獲取簽名
mark

如果出現找不到類

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測試亂碼

發現一個問題 如果eclipse編碼格式是utf-8 都要亂碼 很奇怪