上一篇我們學習了《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;
}
複製程式碼