c++調(diào)用Java靜態(tài)方法
- 示例代碼
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
callJavaStaticMethod();
}
//創(chuàng)建本地方法
public static native void callJavaStaticMethod();
}
//創(chuàng)建提供給 c++調(diào)用的類以及方法
public class ClassMethod {
private static void callStaticMethod(String str, int i) {
Log.d("JNILOG", "ClassMethod == " + str + ", int = " + i);
}
}
//在 c++代碼中調(diào)用
extern "C" JNIEXPORT void JNICALL
Java_com_sensetime_qinhaihang_1vendor_jnidemo5_MainActivity_callJavaStaticMethod(JNIEnv *env,
jclass type) {
//從 classPath 路徑搜索ClassMethod這個類庵寞,返回class對象
jclass findClass = env->FindClass("com/sensetime/qinhaihang_vendor/jnidemo5/ClassMethod");
if(findClass == NULL){
LOGE("find ClassMethod class is null");
return;
}
//從 class 中查找 callStaticMethod 方法
jmethodID midStatidMethod = env->GetStaticMethodID(findClass, "callStaticMethod", "(Ljava/lang/String;I)V");
if(midStatidMethod == NULL){
LOGE("cannt find callStaticMethod!");
return;
}
//調(diào)用 findClass 類里面的靜態(tài)方法
jstring paramStr = env->NewStringUTF("I am static method!");
env->CallStaticVoidMethod(findClass,midStatidMethod,paramStr,100);
//刪除引用
env->DeleteLocalRef(findClass);
env->DeleteLocalRef(paramStr);
}
代碼分析
主要看 CallStaticVoidMethod 函數(shù):
void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
- clazz : 調(diào)用的靜態(tài)方法的 class 對象
- methodID :方法ID(因為一個類里面有多個方法狸相,需要一個唯一標(biāo)識符來確定用哪個方法)
- 。捐川。脓鹃。 : 方法實參
虛擬機針對所有的數(shù)據(jù)類型的返回值都有相關(guān)的函數(shù)定義,Jni提供了不同返回值類型函數(shù):
- CallStaticIntMethod古沥、CallStaticFloatMethod将谊、CallStaticShortMethod、CallStaticObjectMethod等
- 引用類型統(tǒng)一使用 CallStaticObjectMethod
- 每種返回值都提供了三種實參類型的實現(xiàn)
//接收可變參數(shù)列表 void CallStaticXXXMethod(jclass clazz, jmethodID methodID, ...) //接收 va_list參數(shù) void CallStaticXXXMethodV(jclass clazz, jmethodID methodID, va_list args) //接收 const jvalue* args void CallStaticXXXMethodA(jclass clazz, jmethodID methodID, const jvalue* args)
方法簽名
ps: Java中的重載方法渐白,則 methodID 如何體現(xiàn)尊浓?
函數(shù) GetStaticMethodID 中的第三個參數(shù)分析:
env->GetStaticMethodID(findClass, "callStaticMethod", "(Ljava/lang/String;I)V");
第三個參數(shù)是方法簽名,上述例子中獲取 jmethodID 纯衍,會因為 Java 中的重載方法而受到影響栋齿,所以方法簽名主要是用來解決這個問題。
方法簽名的格式為:
++(形參參數(shù)類型列表)返回值襟诸。形參參數(shù)列表中瓦堵,引用類型以 L 開頭,后面緊跟類的全路徑名(需將.全部替換成/)歌亲,以分號 菇用;結(jié)尾。++
示例:
Method Descriptor | Java Language Type |
---|---|
"()Ljava/lang/String;" | String f(); |
"(ILjava/lang/String;)J" | long f(int i,Class c); |
"([B)V" | String(byte[] bytes); |
Java 基本類型與方法簽名中參數(shù)類型和返回值類型的映射關(guān)系:
Field Descriptor | Java Language Type |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
Ps: 如果需要調(diào)用的方法是 空參數(shù)陷揪,則方法簽名是 “ ()V ” 惋鸥。
c++ 中調(diào)用實例方法
代碼示例:
extern "C" JNIEXPORT void JNICALL
Java_com_sensetime_qinhaihang_1vendor_jnidemo5_MainActivity_callJavaInstanceMethod(JNIEnv *env,
jclass type) {
//同樣需要從 classPath 路徑搜索到 對應(yīng)的類的 calss對象
jclass pJclass = env->FindClass("com/sensetime/qinhaihang_vendor/jnidemo5/ClassMethod");
//同樣也需要做校驗
if(pJclass == NULL){
LOGE("find ClassMethod class is null");
return;
}
//獲取類的默認構(gòu)造方法的ID
//<init>代表類的構(gòu)造方法名稱,()V代表無參無返回值的構(gòu)造方法(即默認構(gòu)造方法)
jmethodID midConstruct = env->GetMethodID(pJclass, "<init>", "()V");
if(midConstruct == NULL){
LOGE(" default construct has not悍缠!");
return;
}
//查找實例方法ID
jmethodID midMethod = env->GetMethodID(pJclass, "callInstanceMethod", "(Ljava/lang/String;I)V");
if(midMethod == NULL){
LOGE("cannt find callInstanceMethod ID!");
return;
}
//創(chuàng)建該類的實例
jobject newObject = env->NewObject(pJclass, midConstruct);
if(newObject == NULL){
LOGE("cannt create new instance!");
return;
}
//調(diào)用該類下的方法
jstring pJstring = env->NewStringUTF("我是實例方法");
env->CallVoidMethod(newObject,midMethod,pJstring,200);
//刪除局部引用變量
env->DeleteLocalRef(pJclass);
env->DeleteLocalRef(newObject);
env->DeleteLocalRef(pJstring);
}
參考鏈接:
http://wiki.jikexueyuan.com/project/jni-ndk-developer-guide/function.html
C++中訪問Java類的實例變量
Java類
public class ClassField {
private static int num;
private String str;
public int getNum() {
return num;
}
public void setNum(int num) {
ClassField.num = num;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
代碼示例:
extern "C" JNIEXPORT void JNICALL
Java_com_sensetime_qinhaihang_1vendor_jnidemo5_MainActivity_acessInstanceField(
JNIEnv *env, jclass type, jobject obj) {
//獲取需要訪問的類的 class引用
jclass objectClass = env->GetObjectClass(obj);
if (objectClass == NULL) {
LOGE("getObjectClass error!");
return;
}
//獲取需要訪問的類的實例變量(str)的屬性ID
//第二個參數(shù)為變量名
//第三個參數(shù)為變量參數(shù)類型簽名
jfieldID fieldID = env->GetFieldID(objectClass, "str", "Ljava/lang/String;");
if (fieldID == NULL) {
LOGE("getFieldID error!");
return;
}
//獲取實例變量 str 的值
jstring jstr = (jstring) env->GetObjectField(objectClass, fieldID);
//將 Unicode 編碼的java字符串轉(zhuǎn)換成c風(fēng)格的字符串
const char *cstr = env->GetStringUTFChars(jstr, NULL);
if(cstr == NULL){
LOGE("GetStringUTFChars error!");
return;
}
LOGD("In C ---> ClassField.str = %s\n",cstr);
env->ReleaseStringUTFChars(jstr,cstr);
//修改實例變量 str 的值
jstring newString = env->NewStringUTF("This is cpp String");
if(newString == NULL){
LOGD("NewStringUTF error!");
return;
}
//第一個參數(shù)需要是函數(shù)傳進來的 object 類
env->SetObjectField(obj,fieldID,newString);
env->DeleteLocalRef(objectClass);
env->DeleteLocalRef(jstr);
env->DeleteLocalRef(newString);
}
C++中訪問靜態(tài)變量
代碼示例
extern "C"
JNIEXPORT void JNICALL
Java_com_sensetime_qinhaihang_1vendor_jnidemo5_MainActivity_acessStaticField(JNIEnv *env,
jclass type) {
//獲取對應(yīng)類的class引用
jclass findClass = env->FindClass("com/sensetime/qinhaihang_vendor/jnidemo5/ClassField");
if(findClass == NULL){
LOGE("FindClass error!");
return;
}
//獲取靜態(tài)變量的 ID
jfieldID fID = env->GetStaticFieldID(findClass, "num", "I");
if(fID == NULL){
LOGE("GetStaticFieldID error!");
return;
}
//獲取靜態(tài)變量的值
jint num = env->GetStaticIntField(findClass, fID);
LOGD("In c ---> %d\n",num);
//修改靜態(tài)變量的值
env->SetStaticIntField(findClass,fID,200);
env->DeleteLocalRef(findClass);
}
總結(jié)
- 由于 JNI 函數(shù)是直接操作J VM 中的數(shù)據(jù)結(jié)構(gòu)卦绣,不受 Java 訪問修飾符的限制。即飞蚓,在本地代碼中可以調(diào)用 JNI 函數(shù)可以訪問 Java 對象中的非 public 屬性和方法
- 不管是訪問靜態(tài)方法還是實例方法滤港、靜態(tài)變量還是實例變量,都需要獲取到對應(yīng)的類的 Class引用趴拧。
- 通過Class引用獲取對應(yīng)的 ID溅漾。
- 再通過對應(yīng)的 Class引用 山叮、ID,獲取對應(yīng)的方法以及變量的值添履。
在 Java 中任何一個類的.class字節(jié)碼文件被加載到內(nèi)存中之后聘芜,該class子節(jié)碼文件統(tǒng)一使用 Class 類來表示該類的一個引用(相當(dāng)于 Java 中所有類的基類是 Object一樣)。然后就可以從該類的 Class 引用中動態(tài)的獲取類中的任意方法和屬性缝龄。注意:Class 類在 Java SDK 繼承體系中是一個獨立的類挂谍,沒有繼承自 Object叔壤。因此口叙,通過 Java 反射機制,動態(tài)的獲取一個類的私有實例變量的值俺亮。所以在于C++混合開發(fā)的時候,也是通過Class來對 Java 的方法以及變量進行操作脚曾。
一些方法的總結(jié)
在Jni中能獲取到 Java 類 Class 引用的函數(shù)是:
- FindClass(/路徑): </br>
參數(shù)是 類在工程中的路徑. - GetObjectClass(類對象):<.br>
參數(shù)是 jobject启具,傳入的是對應(yīng) Java 類的實例對象。在需要獲取類的實例對象的時候需要用到這個方法才能獲取對應(yīng)變量的值鲁冯。(簡單的測試之后得到的結(jié)果)