在上一個隨筆中介紹了怎樣實現一個簡單的Jni小程序。在這一篇里主要是說一下JAVA與C++之間的參數傳遞問題。
本人是個Java程序員,工作沒幾年 菜鳥級別,C++不是很熟悉,但對.NET到是了解一些,所以這里面的C++部分都用C++.net來講的。為了便於理解,文檔中可能會有很多通俗易懂的白話,最近也是項目中用到了Jni 才學習了幾天,所以這里要是有哪說得不對,還請大家見諒,發現問題就指出來,大家一起學習 哈!
先大致回顧下上一篇的內容,在上一篇里我們創建了一個JAVA類Test.java和C++.net的Jin01項目。主要是實現Jin01中輸出“第一個Jni小程序”,然后用Test.java來調用。(詳情請見上一篇內容)
Jni01中的函數是:
#include "stdafx.h"
#include "WINSCARD.H"
#include "com_test01_Test.h"
using namespace System;
int main(array<System::String ^> ^args) {
Console::WriteLine(L"Hello World");
return 0;
}
//實現Java_com_test01_Test_firstTest方法
JNIEXPORT void JNICALL Java_com_test01_Test_firstTest(JNIEnv *, jobject){
Console::WriteLine(L"第一個Jni小程序");
}
Test.java代碼如下:
public class Test {
public native void firstTest();//
public static void main(String[] args) {
System.loadLibrary("Jni01");
Test t=new Test();
t.firstTest();
}
}
1、com_test01_Test.h頭文件中實現Jni接口方法深入研究
在上一篇的例子中實現Jni接口的方法聲明在com_test01_Test.h頭文件中,代碼如下:(以下按照字體顏色來解釋每個部分的意思)
JNIEXPORT void JNICALL Java_com_test01_Test_firstTest (JNIEnv * env, jobject obj);
(1)JNIEXPORT :在Jni編程中所有本地語言實現Jni接口的方法前面都有一個"JNIEXPORT",這個可以看做是Jni的一個標志,至今為止沒發現它有什么特殊的用處。
(2)void :這個學過編程的人都知道,當然是方法的返回值了。
(3)JNICALL :這個可以理解為Jni 和Call兩個部分,和起來的意思就是 Jni調用XXX(后面的XXX就是JAVA的方法名)。
(4)Java_com_test01_Test_firstTest:這個就是被上一步中被調用的部分,也就是Java中的native 方法名,這里起名字的方式比較特別,是:包名+類名+方法名。
(5)JNIEnv * env:這個env可以看做是Jni接口本身的一個對象,在上一篇中提到的jni.h頭文件中存在着大量被封裝好的函數,這些函數也是Jni編程中經常被使用到的,要想調用這些函數就需要使用JNIEnv這個對象。例如:env->GetObjectClass()。(詳情請查看jni.h)
(6)jobject obj:剛才在Test類的main方法中有這樣一段代碼:
Test t=new Test(); t.firstTest();
這個jobject需要兩種情況分析。上段代碼中firstTest方法是一個非靜態方法,在Java中要想調用它必須先實例化對象,然后再用對象調用它,那這個時候jobject就可以看做Java類的一個實例化對象,也就是obj就是t。如果firstTest是一個靜態方法,那么在Java中,它不是屬於一個對象的,而是屬於一個類的,Java中用Test.firstTest()這樣的方式來調用,這個時候jobject就可以看做是java類的本身,也就是obj就是Test.class。
2、Jni中的數據類型
每一個Java的數據類型在Jni中都一個和它相對應的數據庫類型,這樣才能保證Java調用C或者C++的過程中數據的正確性。
打開Jni.h文件,有如下代碼:
這里聲明了所有Jni支持的數據類型,可以發現一個規律所有Jni的數據類型前面都有一個”J“字母,這樣主要是為了好記。在Java中所有的對象都以引用的形式體現的,為了保持一致 所以在C與C++中使用了指針類型。Java與Jni中數據類型的對照表如下:
3、實例:在C++.net程序中改變Java中變量的值
在原有Test.java中聲明一個整形變量message,如下:
public class Test {
public native void firstTest();//第一個Jni
public int message;
public static void main(String[] args) {
System.loadLibrary("Jni01");
new Test().firstTest();
}
}
在Jni01的 Java_com_test01_Test_firstTest方法中寫如下代碼:
JNIEXPORT void JNICALL Java_com_test01_Test_firstTest(JNIEnv * env, jobject obj){
jclass class_Test=env->GetObjectClass(obj); //注釋(1)
jfieldID fid_msg=env->GetFieldID(class_Test,"message","I");//注釋(2)
env->SetIntField(obj,fid_msg,123);//注釋(3)
}
(1)調用GetObjectClass方法來獲取Jclass,GetObjectClass的參數就是obj
(2)調用GetFieldID方法來獲取jfieldID,這里要說明一下Jni的所有操作,其實就是操作方法或者是操作屬性兩種。操作方法時需要根據方法的ID(jmethodID)來操作,可以理解為jmethodID標識了這個方法,也就是通過這個jmethodID可以找到你要找的方法。同理操作屬性時也要根據該屬性的ID(jfieldID )來操作。上面那段代碼里我們要改變 變量message的值,所以要先獲取該變量的jfieldID 。獲取變量的jfieldID 方法是GetFieldID。GetFieldID需要3個參數。第一個是上一步獲取的Jclass,第二個參數是Java中的變量名,最后一個參數是變量簽名(int 的變量簽名是”I“)。
下面是所有Jni中的變量簽名列表:
(3)最后調用SetIntField方法就可以設置變量 message的值了。在JAVA的測試環境中打印一下變量的值就知道是否成功了。
全部完成后,可以自己試驗下使用GetIntField方法將message的值取出來。對於這些Jni.h中的方法就不過多的說了,自己看就應該能會用。在以后的隨筆里會多說一些數據轉換的問題。