這篇文章主要寫了本地方法訪問(wèn)JAVA的屬性、靜態(tài)屬性、方法、靜態(tài)方法。
還有一些相關(guān)的小知識(shí)點(diǎn)在補(bǔ)充中有介紹。
Java代碼
public class JniTest {
public static String static_key = "static key";
public String key = "key";
public static int static_num = 110;
public int num = 110;
public native static String getStringFromC();
public native String getString2FromC(int i);
public native void accessField();
public native void accessStaticField();
public native void accessMethod();
public native void accessStaticMethod();
public static void main(String[] args) {
System.out.println("\n\n---getStringFromC-----------------------");
String text1 = getStringFromC();
System.out.println(text1);
System.out.println("\n\n----getString2FromC----------------------");
JniTest j = new JniTest();
String text2 = j.getString2FromC(5);
System.out.println(text2);
System.out.println("\n\n---accessField------------------------");
j.accessField();
System.out.println("修改過(guò)后的變量為" + j.key);
System.out.println("\n\n----accessStaticField-----------------------");
j.accessStaticField();
System.out.println("修改過(guò)后的靜態(tài)變量為" + static_num);
System.out.println("\n\n----accessMethod-----------------------");
j.accessMethod();
System.out.println("\n\n----accessStaticMethod-----------------------");
j.accessStaticMethod();
}
static{
System.loadLibrary("JniTest1");
}
//產(chǎn)生指定范圍的隨機(jī)數(shù)
public int genRandomInt(int max){
System.out.println("genRandomInt 執(zhí)行了...");
return new Random().nextInt(max);
}
//產(chǎn)生UUID字符串
public static String getUUID(){
return UUID.randomUUID().toString();
}
}
C語(yǔ)言代碼
#define _CRT_SECURE_NO_WARNINGS
#include "JniTest.h"
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
JNIEXPORT jstring JNICALL Java_JniTest_getStringFromC
(JNIEnv * env, jclass jcla) {
return (*env)->NewStringUTF(env,"String From C");
}
JNIEXPORT jstring JNICALL Java_JniTest_getString2FromC
(JNIEnv *env, jobject jobj, jint num) {
//此處并沒(méi)有使用num,讀者可以自己實(shí)驗(yàn)
return (*env)->NewStringUTF(env, "String From C2");;
}
JNIEXPORT void JNICALL Java_JniTest_accessField
(JNIEnv *env, jobject jobj) {
jclass jcla = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetFieldID(env, jcla, "key", "Ljava/lang/String;");
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
//修改局部變量
char *c_str = (*env)->GetStringUTFChars(env, jstr, NULL);
char text[20] = "super";
strcat(text, c_str);
jstring new_str = (*env)->NewStringUTF(env, text);
(*env)->SetObjectField(env, jobj, fid, new_str);
}
JNIEXPORT void JNICALL Java_JniTest_accessStaticField
(JNIEnv *env, jobject jobj) {
jclass jcla = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetStaticFieldID(env, jcla, "static_num", "I");
jint num = (*env)->GetStaticIntField(env, jcla, fid);
num = 119;
(*env)->SetStaticIntField(env, jcla, fid, num);
}
JNIEXPORT void JNICALL Java_JniTest_accessMethod
(JNIEnv *env, jobject jobj) {
jclass jcla = (*env)->GetObjectClass(env,jobj);
jmethodID mid = (*env)->GetMethodID(env, jcla, "genRandomInt", "(I)I");
jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
printf("random num:%ld", random);
}
JNIEXPORT void JNICALL Java_JniTest_accessStaticMethod
(JNIEnv *env, jobject jobj) {
jclass jcla = (*env)->GetObjectClass(env, jobj);
jmethodID mid = (*env)->GetStaticMethodID(env, jcla, "getUUID", "()Ljava/lang/String;");
jstring uuid = (*env)->CallStaticObjectMethod(env, jcla, mid);
char *uuid_str = (*env)->GetStringUTFChars(env, uuid, JNI_FALSE);
//拼接
char filename[100];
sprintf(filename, "D://%s.txt", uuid_str);
FILE *fp = fopen(filename, "w");
fputs("i love jason", fp);
fclose(fp);
}
以上代碼分別為:
- JAVA方法調(diào)用無(wú)參本地方法。
- JAVA方法調(diào)用有參的本地方法。
- 本地方法訪問(wèn)并修改JAVA中的屬性
- 本地方法訪問(wèn)并修改JAVA中的靜態(tài)屬性
- 本地方法調(diào)用JAVA中的方法
- 本地方法調(diào)用JAVA中的靜態(tài)方法
讀者手?jǐn)]一遍代碼應(yīng)該就能理解。

調(diào)用結(jié)果

生成的UUID文件
這個(gè)地方有個(gè)問(wèn)題:就是random num:60這里為什么會(huì)比accessStaticMethod后打印。還需要研究一下。
補(bǔ)充
當(dāng)你在JAVA中聲明本地方法時(shí),聲明的是靜態(tài)方法則本地方法屬于類,傳入的是jclass,當(dāng)聲明的是普通方法時(shí),本地方法屬于該類的實(shí)例化對(duì)象,傳入的是jobject。
-
在獲取JAVA屬性的時(shí)候需要傳入屬性名稱的字符串,還需要傳入屬性的簽名,如下圖中的 "Ljava/lang/String;" 就是簽名。注意分號(hào)
jfieldID fid = (*env)->GetFieldID(env, jcla, "key", "Ljava/lang/String;");

簽名
當(dāng)本地方法訪問(wèn)JAVA的屬性時(shí),需要寫簽名,基本類型寫的就是 “Z”,“B”這種,如果屬性是一個(gè)對(duì)象,則需要寫L+全類名;例如“Ljava/lang/String;”。
當(dāng)本地方法訪問(wèn)的是JAVA的方法時(shí),如訪問(wèn)上文中public int genRandomInt(int max);這個(gè)方法的簽名就是"(I)I";
訪問(wèn) public static String getUUID();這個(gè)方法時(shí),簽名是"()Ljava/lang/String;"
即先寫括號(hào),括號(hào)內(nèi)寫參數(shù)的前面,括號(hào)后面跟著寫返回值的簽名。
不會(huì)寫的也可以偷懶直接生成。
Jni開發(fā)補(bǔ)充:怎么獲取簽名
-
這個(gè)函數(shù)這里的NULL;
char *c_str = (*env)->GetStringUTFChars(env, jstr, NULL);
在VS中按F12打開函數(shù)的原型。
const char* (JNICALL *GetStringUTFChars)(JNIEnv *env, jstring str, jboolean *isCopy);
注意看,這里的isCopy是一個(gè)布爾類型的指針。此處的iscopy是一個(gè)傳出參數(shù),不是傳入?yún)?shù),目的是讓程序員知道該字符串是否復(fù)制了,至于是否復(fù)制是由Jni自己決定的。
- 在尋找某個(gè)屬性或者方法的時(shí)候如果尋找不到,會(huì)拋出一個(gè)“異常"(錯(cuò)誤)。
這個(gè)地方拋出的其實(shí)是一個(gè)Throwable,所以在JAVA中catch的時(shí)候要catch Throwable。關(guān)于異常這塊這里只做簡(jiǎn)單的了解,后面會(huì)有單獨(dú)的章節(jié)做介紹。