導航:首頁 > 編程語言 > c調用java函數

c調用java函數

發布時間:2022-06-22 07:19:22

❶ 如何在c++中調用java代碼

對於本地函數
JNIEXPORT void JNICALL Java_video1_TestNative_sayHello(JNIEnv * env, jobject obj)
{
cout<<"Hello Native Test !"<<endl;
}

JNIEnv類型代表Java環境。通過這個JNIEnv*指針,就可以對Java端的代碼進行操作。如,創建Java類得對象,調用Java對象的方法,獲取Java對象的屬性等。
JNIEnv的指針會被JNI傳送到本地方法的實現函數中來對Java端的代碼進行操作

JNIEnv類中的函數:
NewObject/NewString/New<TYPE>Array :new新對象
Get/Set<TYPE>Field:獲取屬性
Get/SetStatic<TYPE>Field :獲取靜態屬性
Call<TYPE>Method/CallStatic<TYPE>Method:調用方法

❷ 如何用C語言調用JAVA的類文件

Java可以通過JNI調用本地C語言方法,而本地C語言方法是以庫文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX機器上是SO文件形式),通過調用本地的庫文件的內部方法,使Java可以實現和本地機器的緊密聯系,調用系統級的各介面方法。

❸ 寫一段java代碼,調用c程序,都有什麼辦法

JAVA以其跨平台的特性深受人們喜愛,而又正由於它的跨平台的目的,使得它和本地機器的各種內部聯系變得很少,約束了它的功能。解決JAVA對本地操作的一種方法就是JNI。
JAVA通過JNI調用本地方法,而本地方法是以庫文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX機器上是SO文件形式)。通過調用本地的庫文件的內部方法,使JAVA可以實現和本地機器的緊密聯系,調用系統級的各介面方法。
簡單介紹及應用如下:
一、JAVA中所需要做的工作
在JAVA程序中,首先需要在類中聲明所調用的庫名稱,如下:
static
{
System.loadLibrary(「goodluck」);
}
在這里,庫的擴展名字可以不用寫出來,究竟是DLL還是SO,由系統自己判斷。
還需要對將要調用的方法做本地聲明,關鍵字為native。並且只需要聲明,而不需要具體實現。如下:
public
native
static
void
set(int
i);
public
native
static
int
get();
然後編譯該JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就會生成C/C++的頭文件。
例如程序testdll.java,內容為:
public
class
testdll
{
static
{
System.loadLibrary("goodluck");
}
public
native
static
int
get();
public
native
static
void
set(int
i);
public
static
void
main(String[]
args)
{
testdll
test
=
new
testdll();
test.set(10);
System.out.println(test.get());
}
}
用javac
testdll.java編譯它,會生成testdll.class。
再用javah
testdll,則會在當前目錄下生成testdll.h文件,這個文件需要被C/C++程序調用來生成所需的庫文件。
二、C/C++中所需要做的工作
對於已生成的.h頭文件,C/C++所需要做的,就是把它的各個方法具體的實現。然後編譯連接成庫文件即可。再把庫文件拷貝到JAVA程序的路徑下面,就可以用JAVA調用C/C++所實現的功能了。
接上例子。我們先看一下testdll.h文件的內容:
/*
DO
NOT
EDIT
THIS
FILE
-
it
is
machine
generated
*/
#include
<jni.h>
/*
Header
for
class
testdll
*/
#ifndef
_Included_testdll
#define
_Included_testdll
#ifdef
__cplusplus
extern
"C"
{
#endif
/*
*
Class:
testdll
*
Method:
get
*
Signature:
()I
*/
JNIEXPORT
jint
JNICALL
Java_testdll_get
(JNIEnv
*,
jclass);
/*
*
Class:
testdll
*
Method:
set
*
Signature:
(I)V
*/
JNIEXPORT
void
JNICALL
Java_testdll_set
(JNIEnv
*,
jclass,
jint);
#ifdef
__cplusplus
}
#endif
#endif
在具體實現的時候,我們只關心兩個函數原型
JNIEXPORT
jint
JNICALL
Java_testdll_get
(JNIEnv
*,
jclass);

JNIEXPORT
void
JNICALL
Java_testdll_set
(JNIEnv
*,
jclass,
jint);
這里JNIEXPORT和JNICALL都是JNI的關鍵字,表示此函數是要被JNI調用的。而jint是以JNI為中介使JAVA的int類型與本地的int溝通的一種類型,我們可以視而不見,就當做int使用。函數的名稱是JAVA_再加上java程序的package路徑再加函數名組成的。參數中,我們也只需要關心在JAVA程序中存在的參數,至於JNIEnv*和jclass我們一般沒有必要去碰它。
好,下面我們用testdll.cpp文件具體實現這兩個函數:
#include
"testdll.h"
int
i
=
0;
JNIEXPORT
jint
JNICALL
Java_testdll_get
(JNIEnv
*,
jclass)
{
return
i;
}
JNIEXPORT
void
JNICALL
Java_testdll_set
(JNIEnv
*,
jclass,
jint
j)
{
i
=
j;
}
編譯連接成庫文件,本例是在WINDOWS下做的,生成的是DLL文件。並且名稱要與JAVA中需要調用的一致,這里就是goodluck.dll
把goodluck.dll拷貝到testdll.class的目錄下,java
testdll運行它,就可以觀察到結果了。

❹ 如何用java調用c語言寫的函數

我知道的有兩種方法,一種是直接用JNI,另一種是則是用Jacob(實質上也用的是JNI)
先講講第一種方法
1.編寫Java程序TestDll,注意,這個類有兩個作用,一個是用來做頭文件,另外一個作用就是通過它來調用dll
public class TestDll {
static
{
System.loadLibrary("DLLSample");//載入dll
}
public native static int DoubleValue(int i);//函數聲明
}
2.編譯: javac TestDll
3.生成頭文件: javah TestDll
生成TestDll.h文件,這裡面只對函數DoubleValue作了聲明
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestDll */
#ifndef _Included_TestDll
#define _Included_TestDll
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestDll
* Method: DoubleValue
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_TestDll_DoubleValue
(JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif

4.用VC製作DLL
修改DLLSample工程,程序中添加函數DoubleValue的實現,函數名必須用jni規定格式,可以照到.h文件里的聲
明來寫:
#include "jni_md.h"
#include "TestDll.h"
JNIEXPORT jint JNICALL Java_TestDll_DoubleValue (JNIEnv *, jclass, jint p)
{
int j = p*2;
return j;

JNIEXPORT,JNICALL,JNIEnv *, jclass請都不要動,jint對應是java里的int
別忘了把TestDll.h,jni.h(這個文件在JDK/include下),jni_md.h(這個文件在jdk/include/win32下)復制到VC
工程目錄中,然後編譯生成dll
5.把生成的DLLSample.dll復制到jdk/bin下(確保PATH指向了這個目錄)
6.應用
我看到他們舉的例子都是把調用寫在了main里, 在這里我把應用取出來放在一個Servlet類中的測試的,同樣可
以得到正確結果,這里體現國TestDll的第二個作用,它是調用dll的一個紐帶
......
TestDll td = new TestDll();
System.out.println("result= " td.DoubleValue(25));
.....

注意:
如果更改了TestDll的內容或重使命名了這個文件,必須重新用它生成頭文件,在dll里也要做相應修改,
因為必須要和java裡面的聲明一致才行

----------------------------------------
本文隨寫,若有什麼錯誤,請多多指出,謝謝

說明:本文是west263的博文,作者並沒有留名

❺ 為什麼C語言有函數聲明才能調用,二Java不用函數聲明就可以直接調用(希望把原理說的清楚一些)

(1)一種是需要建立存儲空間的(定義、聲明)。例如:inta在聲明的時候就已經建立了存儲空間。(2)另一種是不需要建立存儲空間的(聲明)。例如:externinta其中變數a是在別的文件中定義的。例如:在主函數中intmain(){externintA;//這是個聲明而不是定義,聲明A是一個已經定義了的外部變數//注意:聲明外部變數時可以把變數類型去掉如:externA;}intA;//是定義,定義了A為整型的外部變數(全局變數)例如:輸入兩個實數,用一個函數求出它們之和。#includeintmain(){floatadd(floatx,floaty);floata,b,c;printf("Pleaseenteraandb:");scanf("%f,%f",&a,&b);c=add(a,b);printf("sumis%f\n",c);return0;}floatadd(floatx,floaty){floatz;z=x+y;return(z);}

❻ 如何在C/C++中調用Java

如何在C/C++中調用Java

java跨平台的特性使Java越來越受開發人員的歡迎,但也往往會聽到不少的抱怨:用Java開發的圖形用戶窗口界面每次在啟動的時候都會跳出一個控制台窗口,這個控制台窗口讓本來非常棒的界面失色不少。怎麼能夠讓通過Java開發的GUI程序不彈出Java的控制台窗口呢?其實現在很多流行的開發環境例如JBuilder、Eclipse都是使用純Java開發的集成環境。這些集成環境啟動的時候並不會打開一個命令窗口,因為它使用了JNI(Java Native Interface)的技術。通過這種技術,開發人員不一定要用命令行來啟動Java程序,可以通過編寫一個本地GUI程序直接啟動Java程序,這樣就可避免另外打開一個命令窗口,讓開發的Java程序更加專業。
JNI答應運行在虛擬機的Java程序能夠與其它語言(例如C和C++)編寫的程序或者類庫進行相互間的調用。同時JNI提供的一整套的API,答應將Java虛擬機直接嵌入到本地的應用程序中。圖1是Sun站點上對JNI的基本結構的描述。
如何在C/C++中調用Java 三聯
本文將介紹如何在C/C++中調用Java方法,並結合可能涉及到的問題介紹整個開發的步驟及可能碰到的難題和解決方法。本文所採用的工具是Sun公司創建的 Java Development Kit (JDK) 版本 1.3.1,以及微軟公司的Visual C++ 6開發環境。
環境搭建
為了讓本文以下部分的代碼能夠正常工作,我們必須建立一個完整的開發環境。首先需要下載並安裝JDK 1.3.1,其下載地址為「http://java.sun.com」。假設安裝路徑為C:JDK。下一步就是設置集成開發環境,通過Visual C++ 6的菜單Tools→Options打開選項對話框如圖2。

將目錄C:JDKinclude和C:JDKincludewin32加入到開發環境的Include Files目錄中,同時將C:JDKlib目錄添加到開發環境的Library Files目錄中。這三個目錄是JNI定義的一些常量、結構及方法的頭文件和庫文件。集成開發環境已經設置完畢,同時為了執行程序需要把Java虛擬機所用到的動態鏈接庫所在的目錄C:JDK jreinclassic設置到系統的Path環境變數中。這里需要提出的是,某些開發人員為了方便直接將JRE所用到的DLL文件直接拷貝到系統目錄下。這樣做是不行的,將導致初始化Java虛擬機環境失敗(返回值-1),原因是Java虛擬機是以相對路徑來尋找所用到的庫文件和其它一些相關文件的。至此整個JNI的開發環境設置完畢,為了讓此次JNI旅程能夠順利進行,還必須先預備一個Java類。在這個類中將用到Java中幾乎所有有代表性的屬性及方法,如靜態方法與屬性、數組、異常拋出與捕捉等。我們定義的Java程序(Demo.java)如下,本文中所有的代碼演示都將基於該Java程序,代碼如下:
package jni.test; /** * 該類是為了演示JNI如何訪問各種對象屬性等 * @author liudong */ public class Demo { //用於演示如何訪問靜態的基本類型屬性 public static int COUNT = 8; //演示對象型屬性 public String msg; PRivate int[] counts; public Demo() { this("預設構造函數"); } /** * 演示如何訪問構造器 */ public Demo(String msg) { System.out.println(":" + msg); this.msg = msg; this.counts = null; } /** * 該方法演示如何訪問一個訪問以及中文字元的處理 */ public String getMessage() { return msg; } /** * 演示數組對象的訪問 */ public int[] getCounts() { return counts; } /** * 演示如何構造一個數組對象 */ public void setCounts(int[] counts) { this.counts = counts; } /** * 演示異常的捕捉 */ public void throwExcp() throws IllegalaccessException { throw new IllegalAccessException("exception occur."); } }
初始化虛擬機
本地代碼在調用Java方法之前必須先載入Java虛擬機,而後所有的Java程序都在虛擬機中執行。為了初始化Java虛擬機,JNI提供了一系列的介面函數Invocation API。通過這些API可以很方便地將虛擬機載入到內存中。創建虛擬機可以用函數 jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args)。對於這個函數有一點需要注重的是,在JDK 1.1中第三個參數總是指向一個結構JDK1_ 1InitArgs, 這個結構無法完全在所有版本的虛擬機中進行無縫移植。在JDK 1.2中已經使用了一個標準的初始化結構JavaVMInitArgs來替代JDK1_1InitArgs。下面我們分別給出兩種不同版本的示例代碼。
在JDK 1.1初始化虛擬機:
#include int main() { JNIEnv *env; JavaVM *jvm; JDK1_1InitArgs vm_args; jint res; /* IMPORTANT: 版本號設置一定不能漏 */ vm_args.version = 0x00010001; /*獲取預設的虛擬機初始化參數*/ JNI_GetDefaultJavaVMInitArgs(&vm_args); /* 添加自定義的類路徑 */ sprintf(classpath, "%s%c%s", vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH); vm_args.classpath = classpath; /*設置一些其他的初始化參數*/ /* 創建虛擬機 */ res = JNI_CreateJavaVM(&jvm,&env,&vm_args); if (res < 0) { fprintf(stderr, "Can't create Java VM "); exit(1); } /*釋放虛擬機資源*/ (*jvm)->DestroyJavaVM(jvm); }
JDK 1.2初始化虛擬機:
/* invoke2.c */ #include int main() { int res; JavaVM *jvm; JNIEnv *env; JavaVMInitArgs vm_args; JavaVMOption options[3]; vm_args.version=JNI_VERSION_1_2;//這個欄位必須設置為該值 /*設置初始化參數*/ options[0].optionString = "-Djava.compiler=NONE"; options[1].optionString = "-Djava.class.path=."; options[2].optionString = "-verbose:jni";//用於跟蹤運行時的信息 /*版本號設置不能漏*/ vm_args.version = JNI_VERSION_1_2; vm_args.nOptions = 3; vm_args.options = options; vm_args.ignoreUnrecognized = JNI_TRUE; res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); if (res < 0) { fprintf(stderr, "Can't create Java VM "); exit(1); } (*jvm)->DestroyJavaVM(jvm); fprintf(stdout, "Java VM destory. "); }
為了保證JNI代碼的可移植性,建議使用JDK 1.2的方法來創建虛擬機。JNI_CreateJavaVM函數的第二個參數JNIEnv *env,就是貫穿整個JNI始末的一個參數,因為幾乎所有的函數都要求一個參數就是JNIEnv *env。
訪問類方法
初始化了Java虛擬機後,就可以開始調用Java的方法。要調用一個Java對象的方法必須經過幾個步驟:
1.獲取指定對象的類定義(jclass)
有兩種途徑來獲取對象的類定義:第一種是在已知類名的情況下使用FindClass來查找對應的類。但是要注重類名並不同於平時寫的Java代碼,例如要得到類jni.test.Demo的定義必須調用如下代碼:
jclass cls = (*env)->FindClass(env, "jni/test/Demo");//把點號換成斜杠
然後通過對象直接得到其所對應的類定義:
jclass cls = (*env)-> GetObjectClass(env, obj); //其中obj是要引用的對象,類型是jobject
2.讀取要調用方法的定義(jmethodID)
我們先來看看JNI中獲取方法定義的函數:
jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig); jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char *name, const char *sig);
這兩個函數的區別在於GetStaticMethodID是用來獲取靜態方法的定義,GetMethodID則是獲取非靜態的方法定義。這兩個函數都需要提供四個參數:env就是初始化虛擬機得到的JNI環境;第二個參數class是對象的類定義,也就是第一步得到的obj;第三個參數是方法名稱;最重要的是第四個參數,這個參數是方法的定義。因為我們知道Java中答應方法的多態,僅僅是通過方法名並沒有辦法定位到一個具體的方法,因此需要第四個參數來指定方法的具體定義。但是怎麼利用一個字元串來表示方法的具體定義呢?JDK中已經預備好一個反編譯工具javap,通過這個工具就可以得到類中每個屬性、方法的定義。下面就來看看jni.test.Demo的定義:
打開命令行窗口並運行 javap -s -p jni.test.Demo 得到運行結果如下:
Compiled from Demo.java public class jni.test.Demo extends java.lang.Object { public static int COUNT; /* I */ public java.lang.String msg; /* Ljava/lang/String; */ private int counts[]; /* [I */ public jni.test.Demo(); /* ()V */ public jni.test.Demo(java.lang.String); /* (Ljava/lang/String;)V */ public java.lang.String getMessage(); /* ()Ljava/lang/String; */ public int getCounts()[]; /* ()[I */ public void setCounts(int[]); /* ([I)V */ public void throwExcp() throws java.lang.IllegalAccessException; /* ()V */ static {}; /* ()V */ }
我們看到類中每個屬性和方法下面都有一段注釋。注釋中不包含空格的內容就是第四個參數要填的內容(關於javap具體參數請查詢JDK的使用幫助)。下面這段代碼演示如何訪問jni.test.Demo的getMessage方法:
/* 假設我們已經有一個jni.test.Demo的實例obj */ jmethodID mid; jclass cls = (*env)-> GetObjectClass (env, obj);//獲取實例的類定義 mid=(*env)->GetMethodID(env,cls,"getMessage"," ()Ljava/lang/String; "); /*假如mid為0表示獲取方法定義失敗*/ jstring msg = (*env)-> CallObjectMethod(env, obj, mid); /* 假如該方法是靜態的方法那隻需要將最後一句代碼改為以下寫法即可: jstring msg = (*env)-> CallStaticObjectMethod(env, cls, mid); */
3.調用方法
為了調用對象的某個方法,可以使用函數CallMethod或者CallStaticMethod(訪問類的靜態方法),根據不同的返回類型而定。這些方法都是使用可變參數的定義,假如訪問某個方法需要參數時,只需要把所有參數按照順序填寫到方法中就可以。在講到構造函數的訪問時,將演示如何訪問帶參數的構造函數。
訪問類屬性
訪問類的屬性與訪問類的方法大體上是一致的,只不過是把方法變成屬性而已。
1.獲取指定對象的類(jclass)
這一步與訪問類方法的第一步完全相同,具體使用參看訪問類方法的第一步。
2.讀取類屬性的定義(jfieldID)
在JNI中是這樣定義獲取類屬性的方法的:
jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig); jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
這兩個函數中第一個參數為JNI環境;clazz為類的定義;name為屬性名稱;第四個參數同樣是為了表達屬性的類型。前面我們使用javap工具獲取類的具體定義的時候有這樣兩行:
public java.lang.String msg; /* Ljava/lang/String; */
其中第二行注釋的內容就是第四個參數要填的信息,這跟訪問類方法時是相同的。
3.讀取和設置屬性值
有了屬性的定義要訪問屬性值就很輕易了。有幾個方法用來讀取和設置類的屬性,它們是:GetField、SetField、GetStaticField、SetStaticField。比如讀取Demo類的msg屬性就可以用GetObjectField,而訪問COUNT用GetStaticIntField,相關代碼如下:
jfieldID field = (*env)->GetFieldID(env,obj,"msg"," Ljava/lang/String;"); jstring msg = (*env)-> GetObjectField(env, cls, field);//msg就是對應Demo的msg jfieldID field2 = (*env)->GetStaticFieldID(env,obj,"COUNT","I"); jint count = (*env)->GetStaticIntField(env,cls,field2);
訪問構造函數
很多人剛剛接觸JNI的時候往往會在這一節碰到問題,查遍了整個jni.h看到這樣一個函數NewObject,它應該是可以用來訪問類的構造函數。但是該函數需要提供構造函數的方法定義,其類型是jmethodID。從前面的內容我們知道要獲取方法的定義首先要知道方法的名稱,但是構造函數的名稱怎麼來填寫呢?其實訪問構造函數與訪問一個普通的類方法大體上是一樣的,惟一不同的只是方法名稱不同及方法調用時不同而已。訪問類的構造函數時方法名必須填寫「」。下面的代碼演示如何構造一個Demo類的實例:
jclass cls = (*env)->FindClass(env, "jni/test/Demo"); /** 首先通過類的名稱獲取類的定義,相當於Java中的Class.forName方法 */ if (cls == 0) jmethodID mid = (*env)->GetMethodID(env,cls,"","(Ljava/lang/String;)V "); if(mid == 0) jobject demo = jenv->NewObject(cls,mid,0); /** 訪問構造函數必須使用NewObject的函數來調用前面獲取的構造函數的定義 上面的代碼我們構造了一個Demo的實例並傳一個空串null */
數組處理
創建一個新數組
要創建一個數組,我們首先應該知道數組元素的類型及數組長度。JNI定義了一批數組的類型jArray及數組操作的函數NewArray,其中就是數組中元素的類型。例如,要創建一個大小為10並且每個位置值分別為1-10的整數數組,編寫代碼如下:
int i = 1; jintArray array;//定義數組對象 (*env)-> NewIntArray(env, 10); for(; i<= 10; i++) (*env)->SetIntArrayRegion(env, array, i-1, 1, &i);
訪問數組中的數據
訪問數組首先應該知道數組的長度及元素的類型。現在我們把創建的數組中的每個元素值列印出來,代碼如下:
int i; /* 獲取數組對象的元素個數 */ int len = (*env)->GetArrayLength(env, array); /* 獲取數組中的所有元素 */ jint* elems = (*env)-> GetIntArrayElements(env, array, 0); for(i=0; i< len; i++) printf("ELEMENT %d IS %d ", i, elems[i]);
中文處理
中文字元的處理往往是讓人比較頭疼的事情,非凡是使用Java語言開發的軟體,在JNI這個問題更加突出。由於Java中所有的字元都是Unicode編碼,但是在本地方法中,例如用VC編寫的程序,假如沒有非凡的定義一般都沒有使用Unicode的編碼方式。為了讓本地方法能夠訪問Java中定義的中文字元及Java訪問本地方法產生的中文字元串,我定義了兩個方法用來做相互轉換。
· 方法一,將Java中文字元串轉為本地字元串
/** 第一個參數是虛擬機的環境指針第二個參數為待轉換的Java字元串定義第三個參數是本地存儲轉換後字元串的內存塊第三個參數是內存塊的大小 */ int JStringToChar(JNIEnv *env, jstring str, LPTSTR desc, int desc_len) { int len = 0; if(desc==NULLstr==NULL) return -1; //在VC中wchar_t是用來存儲寬位元組字元(UNICODE)的數據類型 wchar_t *w_buffer = new wchar_t[1024]; ZeroMemory(w_buffer,1024*sizeof(wchar_t)); //使用GetStringChars而不是GetStringUTFChars wcscpy(w_buffer,env->GetStringChars(str,0)); env->ReleaseStringChars(str,w_buffer); ZeroMemory(desc,desc_len); //調用字元編碼轉換函數(Win32 API)將UNICODE轉為ASCII編碼格式字元串 //關於函數WideCharToMultiByte的使用請參考MSDN len = WideCharToMultiByte(CP_ACP,0,w_buffer,1024,desc,desc_len,NULL,NULL); //len = wcslen(w_buffer); if(len>0 && len
· 方法二,將C的字元串轉為Java能識別的Unicode字元串
jstring NewJString(JNIEnv* env,LPCTSTR str) { if(!env !str) return 0; int slen = strlen(str); jchar* buffer = new jchar[slen]; int len = MultiByteToWideChar(CP_ACP,0,str,strlen(str),buffer,slen); if(len>0 && len < slen) buffer[len]=0; jstring js = env->NewString(buffer,len); delete [] buffer; return js; }
異常
由於調用了Java的方法,因此難免產生操作的異常信息。這些異常沒有辦法通過C++本身的異常處理機制來捕捉到,但JNI可以通過一些函數來獲取Java中拋出的異常信息。之前我們在Demo類中定義了一個方法throwExcp,下面將訪問該方法並捕捉其拋出來的異常信息,代碼如下:
/** 假設我們已經構造了一個Demo的實例obj,其類定義為cls */ jthrowable excp = 0;/* 異常信息定義 */ jmethodID mid=(*env)->GetMethodID(env,cls,"throwExcp","()V"); /*假如mid為0表示獲取方法定義失敗*/ jstring msg = (*env)-> CallVoidMethod(env, obj, mid); /* 在調用該方法後會有一個IllegalAccessException的異常拋出 */ excp = (*env)->ExceptionOccurred(env); if(excp){ (*env)->ExceptionClear(env); //通過訪問excp來獲取具體異常信息 /* 在Java中,大部分的異常信息都是擴展類java.lang.Exception,因此可以訪問excp的toString 或者getMessage來獲取異常信息的內容。訪問這兩個方法同前面講到的如何訪問類的方法是相同的。 */ }
線程和同步訪問
有些時候需要使用多線程的方式來訪問Java的方法。我們知道一個Java虛擬機是非常消耗系統的內存資源,差不多每個虛擬機需要內存大約在20MB左右。為了節省資源要求每個線程使用的是同一個虛擬機,這樣在整個的JNI程序中只需要初始化一個虛擬機就可以了。所有人都是這樣想的,但是一旦子線程訪問主線程創建的虛擬機環境變數,系統就會出現錯誤對話框,然後整個程序終止。
其實這裡面涉及到兩個概念,它們分別是虛擬機(JavaVM *jvm)和虛擬機環境(JNIEnv *env)。真正消耗大量系統資源的是jvm而不是env,jvm是答應多個線程訪問的,但是env只能被創建它本身的線程所訪問,而且每個線程必須創建自己的虛擬機環境env。這時候會有人提出疑問,主線程在初始化虛擬機的時候就創建了虛擬機環境env。為了讓子線程能夠創建自己的env,JNI提供了兩個函數:AttachCurrentThread和DetachCurrentThread。下面代碼就是子線程訪問Java方法的框架:
DWord WINAPI ThreadProc(PVOID dwParam) { JavaVM jvm = (JavaVM*)dwParam;/* 將虛擬機通過參數傳入 */ JNIEnv* env; (*jvm)-> AttachCurrentThread(jvm, (void**)&env, NULL); ......... (*jvm)-> DetachCurrentThread(jvm); }
時間
關於時間的話題是我在實際開發中碰到的一個問題。當要發布使用了JNI的程序時,並不一定要求客戶要安裝一個Java運行環境,因為可以在安裝程序中打包這個運行環境。為了讓打包程序利於下載,這個包要比較小,因此要去除JRE(Java運行環境)中一些不必要的文件。但是假如程序中用到Java中的日歷類型,例如java.util.Calendar等,那麼有個文件一定不能去掉,這個文件就是[JRE]lib zmappings。它是一個時區映射文件,一旦沒有該文件就會發現時間操作上經常出現與正確時間相差幾個小時的情況。下面是打包JRE中必不可少的文件列表(以Windows環境為例),其中[JRE]為運行環境的目錄,同時這些文件之間的相對路徑不能變。
文件名目錄 hpi.dll [JRE]in ioser12.dll [JRE]in java.dll [JRE]in net.dll [JRE]in verify.dll [JRE]in zip.dll [JRE]in jvm.dll [JRE]inclassic rt.jar [JRE]lib tzmappings [JRE]lib
由於rt.jar有差不多10MB,但是其中有很大一部分文件並不需要,可以根據實際的應用情況進行刪除。例如程序假如沒有用到Java Swing,就可以把涉及到Swing的文件都刪除後重新打包。

android ndk 開發,C++ 調用Java的方法

  1. Android.mk文件:



LOCAL_SRC_FILES參數用空格隔開

[c-sharp]view plainprint?

LOCAL_PATH:=$(callmy-dir)

include$(CLEAR_VARS)

LOCAL_MODULE:=native

LOCAL_SRC_FILES:=geolo.cppmy_jni.h

include$(BUILD_SHARED_LIBRARY)


2. geolo.cpp

先用FindClass方法找到java類,有點類似java的反射用LoadClass

再用CallObjectMethod方法調用Java類的函數。


[c-sharp]view plainprint?

#include"my_jni.h"

jobjectgetInstance(JNIEnv*env,jclassobj_class){

jmethodIDconstruction_id=env->GetMethodID(obj_class,"<init>","()V");

jobjectobj=env->NewObject(obj_class,construction_id);

returnobj;

}

JNIEXPORTjstringJNICALLJava_com_easepal_geolo_CActivityMain_stringFromJNI(JNIEnv*env,jobjectthiz){

jstringstr;

jclassjava_class=env->FindClass("com/easepal/geolo/CForCall");

if(java_class==0){

returnenv->NewStringUTF("notfindclass!");

}

jobjectjava_obj=getInstance(env,java_class);

if(java_obj==0){

returnenv->NewStringUTF("notfindjavaOBJ!");

}

jmethodIDjava_method=env->GetMethodID(java_class,"GetJavaString","()Ljava/lang/String;");

if(java_method==0){

returnenv->NewStringUTF("notfindjavamethod!");

}

str=(jstring)env->CallObjectMethod(java_obj,java_method);

returnstr;

}

3. my_jni.h


[c-sharp]view plainprint?

/*DONOTEDITTHISFILE-itismachinegenerated*/

#include<jni.h>

/*Headerforclasscom_easepal_geolo_CActivityMain*/

#ifndef_Included_com_easepal_geolo_CActivityMain

#define_Included_com_easepal_geolo_CActivityMain

#ifdef__cplusplus

extern"C"{

#endif

/*

*Class:com_easepal_geolo_CActivityMain

*Method:stringFromJNI

*Signature:()Ljava/lang/String;

*/

JNIEXPORTjstringJNICALLJava_com_easepal_geolo_CActivityMain_stringFromJNI(JNIEnv*,jobject);

#ifdef__cplusplus

}

#endif

#endif


4.CActivityMain.java


[c-sharp]view plainprint?

packagecom.easepal.geolo;

importandroid.app.Activity;

importandroid.os.Bundle;

importandroid.widget.TextView;

{

/**.*/

@Override

publicvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

TextViewtv=newTextView(this);

tv.setText(stringFromJNI("hello"));

setContentView(tv);

}

static{

System.loadLibrary("native");

}

(Stringstr);

}


5.CForCall.java


[c-sharp]view plainprint?

packagecom.easepal.geolo;

publicclassCForCall{

publicCForCall(){};

//public~CForCall(){};

publicStringGetJavaString(){

Stringstr;

str="123456";

returnstr;

}

}

❽ c怎麼調用返回值為String的java函數

應該是CallStaticObjectMethod吧,jni裡面沒有CallStaticStringMethod這個方法

jstring result = (jstring)jnienv->CallObjectMethod();

❾ 如何從C中調用Java函數使用JNI

1. 編寫並編譯J2C.java
import java.lang.management.ManagementFactory;

import java.lang.management.RuntimeMXBean;
public class J2C
{
static
{
try{
// 此處即為本地方法所在鏈接庫名
System.loadLibrary("j2c");
} catch(UnsatisfiedLinkError e)
{
System.err.println( "Cannot load J2C library:\n " +
e.toString() );
}
}
//聲明的本地方法
public static native int write2proc(int pid);
public static void main(String[] args){
//獲取本進程(即主線程)的pid
final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
final String info = runtime.getName();
final int index = info.indexOf("@");

if (index != -1) {
final int pid = Integer.parseInt(info.substring(0, index));
System.out.println(info);
System.out.println(pid);
write2proc(pid);
}

try{
Thread.sleep(8000);
} catch(InterruptedException e){
e.printStackTrace();
}
}
}

note:Java程序中System.loadLibrary參數名表示要載入的C/C++共享庫,第6步生成的共享庫名必須與該參數一致,即System.loadLibrary(Name) 對應共享庫名libName.so (共享庫名必須以lib開頭)
2. 生成C頭文件J2C.h:javah J2C
/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>
/* Header for class J2C */

#ifndef _Included_J2C
#define _Included_J2C
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: J2C
* Method: write2proc
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_J2C_write2proc
(JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif

note:1. 頭文件自動生成,不要修改它;
2. 函數JNIEXPORT jint JNICALL Java_J2C_write2proc(JNIEnv *, jclass, jint);
按照注釋的說明是在J2C.java文件的類J2C的方法write2proc處定義,故C程序的實現函數必須與該處簽名一致;
3. 編寫C程序J2C.c
#include <stdio.h>

#include "J2C.h"
JNIEXPORT int JNICALL Java_J2C_write2proc(JNIEnv * env, jobject arg, jint pid)
{
printf("current pid is %d\n", pid);
return 0;
}

4. 編譯C程序

因為C程序里#include "J2C.h"而J2C.h又#include <jni.h>, 而gcc裡面默認環境並不知道jni.h是什麼東西,故編譯時需要告訴編譯器jni.h的位置( jni.h在jdk 的$JAVA_HOME/include下面),所以才有了上面的編譯參數;
因為使用gcc編譯得到動態庫,在jni調用的時候,某些情況會有異常, 可嘗試改用g++。
總結
1. Java中方法的原型聲明與C/C++對應的實現文件定義必須一致(可以通過自動生成的C/C++頭文件來比較),尤其是類名和方法名;
2. Java中System.loadLibrary()載入的共享庫名必須與後面C/C++生成的共享庫名一致。

❿ C調用java時使用JNI的問題!急。。。。。

M,是其中的一個組成部分,更詳細的看下面:

----------------------------------
操作系統裝入jvm是通過jdk中java.exe來完成,通過下面4步來完成jvm環境.
1.創建jvm裝載環境和配置
2.裝載jvm.dll
3.初始化jvm.dll並掛界到JNIENV(JNI調用介面)實例
4.調用JNIEnv實例裝載並處理class類。

在我們運行和調試java程序的時候,經常會提到一個jvm的概念.jvm是java程序運行的環境,但是他同時一個操作系統的一個應用程序一個進程,因此他也有他自己的運行的生命周期,也有自己的代碼和數據空間.
首先來說一下jdk這個東西,不管你是初學者還是高手,是j2ee程序員還是j2se程序員,jdk總是在幫我們做一些事情.我們在了解java之前首先大師們會給我們提供說jdk這個東西.它在java整個體系中充當著什麼角色呢?我很驚嘆sun大師們設計天才,能把一個如此完整的體系結構化的如此完美.jdk在這個體系中充當一個生產加工中心,產生所有的數據輸出,是所有指令和戰略的執行中心.本身它提供了java的完整方案,可以開發目前java能支持的所有應用和系統程序.這里說一個問題,大家會問,那為什麼還有j2me,j2ee這些東西,這兩個東西目的很簡單,分別用來簡化各自領域內的開發和構建過程.jdk除了jvm之外,還有一些核心的API,集成API,用戶工具,開發技術,開發工具和API等組成
好了,廢話說了那麼多,來點於主題相關的東西吧.jvm在整個jdk中處於最底層,負責於操作系統的交互,用來屏蔽操作系統環境,提供一個完整的java運行環境,因此也就虛擬計算機. 操作系統裝入jvm是通過jdk中java.exe來完成,通過下面4步來完成jvm環境.
1.創建jvm裝載環境和配置
2.裝載jvm.dll
3.初始化jvm.dll並掛界到JNIENV(JNI調用介面)實例
4.調用JNIEnv實例裝載並處理class類。
一.jvm裝入環境,jvm提供的方式是操作系統的動態連接文件.既然是文件那就一個裝入路徑的問題,java是怎麼找這個路徑的呢?當你在調用java test的時候,操作系統會在path下在你的java.exe程序,java.exe就通過下面一個過程來確定jvm的路徑和相關的參數配置了.下面基於windows的實現的分析.
首先查找jre路徑,java是通過GetApplicationHome api來獲得當前的java.exe絕對路徑,c:\j2sdk1.4.2_09\bin\java.exe,那麼它會截取到絕對路徑c:\j2sdk1.4.2_09\,判斷c:\j2sdk1.4.2_09\bin\java.dll文件是否存在,如果存在就把c:\j2sdk1.4.2_09\作為jre路徑,如果不存在則判斷c:\j2sdk1.4.2_09\jre\bin\java.dll是否存在,如果存在這c:\j2sdk1.4.2_09\jre作為jre路徑.如果不存在調用GetPublicJREHome查HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment\「當前JRE版本號」\JavaHome的路徑為jre路徑。
然後裝載jvm.cfg文件JRE路徑+\lib+\ARCH(CPU構架)+\jvm.cfgARCH(CPU構架)的判斷是通過java_md.c中GetArch函數判斷的,該函數中windows平台只有兩種情況:WIN64的『ia64』,其他情況都為『i386』。以我的為例:C:\j2sdk1.4.2_09\jre\lib\i386\jvm.cfg.主要的內容如下:
-client KNOWN
-server KNOWN
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR
在我們的jdk目錄中jre\bin\server和jre\bin\client都有jvm.dll文件存在,而java正是通過jvm.cfg配置文件來管理這些不同版本的jvm.dll的.通過文件我們可以定義目前jdk中支持那些jvm,前面部分(client)是jvm名稱,後面是參數,KNOWN表示jvm存在,ALIASED_TO表示給別的jvm取一個別名,WARN表示不存在時找一個jvm替代,ERROR表示不存在拋出異常.在運行java XXX是,java.exe會通過CheckJvmType來檢查當前的jvm類型,java可以通過兩種參數的方式來指定具體的jvm類型,一種按照jvm.cfg文件中的jvm名稱指定,第二種方法是直接指定,它們執行的方法分別是「java -J」、「java -XXaltjvm=」或「java -J-XXaltjvm=」。如果是第一種參數傳遞方式,CheckJvmType函數會取參數『-J』後面的jvm名稱,然後從已知的jvm配置參數中查找如果找到同名的則去掉該jvm名稱前的『-』直接返回該值;而第二種方法,會直接返回「-XXaltjvm=」或「-J-XXaltjvm=」後面的jvm類型名稱;如果在運行java時未指定上面兩種方法中的任一一種參數,CheckJvmType會取配置文件中第一個配置中的jvm名稱,去掉名稱前面的『-』返回該值。CheckJvmType函數的這個返回值會在下面的函數中匯同jre路徑組合成jvm.dll的絕對路徑。如果沒有指定這會使用jvm.cfg中第一個定義的jvm.可以通過set _JAVA_LAUNCHER_DEBUG=1在控制台上測試.
最後獲得jvm.dll的路徑,JRE路徑+\bin+\jvm類型字元串+\jvm.dll就是jvm的文件路徑了,但是如果在調用java程序時用-XXaltjvm=參數指定的路徑path,就直接用path+\jvm.dll文件做為jvm.dll的文件路徑.
二:裝載jvm.dll
通過第一步已經找到了jvm的路徑,java通過LoadJavaVM來裝入jvm.dll文件.裝入工作很簡單就是調用windows API函數:
LoadLibrary裝載jvm.dll動態連接庫.然後把jvm.dll中的導出函數JNI_CreateJavaVM和JNI_GetDefaultJavaVMInitArgs掛接到InvocationFunctions變數的CreateJavaVM和GetDefaultJavaVMInitArgs函數指針變數上。jvm.dll的裝載工作宣告完成。
三:初始化jvm,獲得本地調用介面,這樣就可以在java中調用jvm的函數了.調用InvocationFunctions->CreateJavaVM也就是jvm中JNI_CreateJavaVM方法獲得JNIEnv結構的實例.
四:運行java程序.
java程序有兩種方式一種是jar包,一種是class. 運行jar,java -jar XXX.jar運行的時候,java.exe調用GetMainClassName函數,該函數先獲得JNIEnv實例然後調用java類java.util.jar.JarFileJNIEnv中方法getManifest()並從返回的Manifest對象中取getAttributes("Main-Class")的值即jar包中文件:META-INF/MANIFEST.MF指定的Main-Class的主類名作為運行的主類。之後main函數會調用java.c中LoadClass方法裝載該主類(使用JNIEnv實例的FindClass)。main函數直接調用java.c中LoadClass方法裝載該類。如果是執行class方法。main函數直接調用java.c中LoadClass方法裝載該類。

然後main函數調用JNIEnv實例的GetStaticMethodID方法查找裝載的class主類中
「public static void main(String[] args)」方法,並判斷該方法是否為public方法,然後調用JNIEnv實例的
CallStaticVoidMethod方法調用該java類的main方法。
另外,虛機團上產品團購,超級便宜

閱讀全文

與c調用java函數相關的資料

熱點內容
app加拿大pc怎麼操控的 瀏覽:698
光影app蘋果怎麼下載不了 瀏覽:971
php會員注冊代碼 瀏覽:511
csgo如何用128tick伺服器 瀏覽:571
百度網度怎麼解壓 瀏覽:946
windowsopencv源碼 瀏覽:945
origin平滑演算法 瀏覽:875
unity程序員簡歷 瀏覽:63
單片機ifelse 瀏覽:695
如何理解php面向對象 瀏覽:96
macword轉pdf 瀏覽:848
python列表求交集 瀏覽:874
解壓包如何轉音頻 瀏覽:447
機明自動編程軟體源碼 瀏覽:326
php埠號設置 瀏覽:541
phperegreplace 瀏覽:320
androidgridview翻頁 瀏覽:537
ssh協議編程 瀏覽:635
如何開我的世界電腦伺服器地址 瀏覽:861
玄關pdf 瀏覽:609