java调用c 图形程序,Java程序和C程序的相互调用

第一章、建立自己的C函式庫

程式在執行時,會自動去連結libc.so,這是基本的c函式庫,也就是compile時不用加上-lc,就會自動進行連結。共享函式庫和靜態函式庫的不同在於,不論多少程序呼叫共享函式庫,它只使用一份記憶體,因此非常節省記憶體空間;並且在修改函式庫後,使用該函式庫的程式並不用重新編譯,只需要編譯函式庫本身即可。共享函式庫的代價就是程式的複雜度提高,若二進位檔搬去別的電腦執行時找不到共享函式庫,則程式會無法執行。

以下是建立"自己"的共享函式庫的步驟。

建立hello.c和hello.h

#include

void helloworld() {

printf("hello world!\n");

}

gcc -fPIC -Wall -c hello.c //編譯時依照機器來產生hello.o,拿到別的機器上hello.o即無效

gcc -shared -o libhello.so hello.o //編譯成libhello.so,加入hello.o

建立caller.c

#include "hello.h"

#include

int main() {

printf("call hello.a\n");

helloworld();

return 1;

}

gcc caller.c -o caller -lhello -L. //編譯時指定連結libhello.so,並且指定目錄在.

接下來有兩種方法可以讓二進位執行檔找到libhello.so:

cp libhello.so /lib/ //直接copy檔案到預設library的目錄下,程式執行時會自動去該目錄找尋

LD_LIBRARY_PATH=`pwd` ./caller //在執行前設定LD_LIBRARY_PATH變數,程式執行時就會去找該目錄下的lib

完成自製的共享函式庫了。

補註:如果想要在一個lib中放入多個object檔案,只要在第三步時,將多個.o檔加在參數之後即可。

ex:gcc -shared -o libhello.so hello.o hello2.o hello3.o

第二章、使用Java Native Interface呼叫C程式

如何讓JAVA程式呼叫以前寫好的C程式?JNI可以在JAVA程式中呼叫不同語言寫成的程式。以下是以C語言為例:

建立JAVA程式,其中宣告native function的格式為:public native void functionname()

class HelloWorld {

public native void helloworld();

static {

System.loadLibrary("hello");

}

public static void main(String[] args) {

new HelloWorld().helloworld();

}

}

javac HelloWorld.java //編譯JAVA程式

javah -jni HelloWorld //產生對應C程式的標頭檔,內容中粗體為C的函式宣告:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include

/* Header for class HelloWorld */

#ifndef _Included_HelloWorld

#define _Included_HelloWorld

#ifdef __cplusplus

extern "C" {

#endif

/*

* Class: HelloWorld

* Method: helloworld

* Signature: ()V

*/

JNIEXPORT void JNICALL Java_HelloWorld_helloworld

(JNIEnv *, jobject);

#ifdef __cplusplus

}

#endif

#endif

實作C函式的內容於HelloWorld.c,以下為其內容:

注意其中兩個參數JNIEnv *和jobject都是預設就會幫你傳的參數,故在java中呼叫native時並不用去理會這兩個參數。

#include

#include "HelloWorld.h"

#include

JNIEXPORT void JNICALL

Java_HelloWorld_helloworld(JNIEnv *env, jobject obj)

{

printf("Hello world!\n");

return;

}

gcc -fPIC HelloWorld.c -c -I/usr/local/j2sdk1.4.2_06/include -I/usr/local/j2sdk1.4.2_06/include/linux //編譯HelloWorld.c成Object file

gcc -shared -o libhello.so HelloWorld.o //產生hello library,命名為libhello.so

LD_LIBRARY_PATH=`pwd` java HelloWorld //執行java HelloWorld之前必需要設定LD_LIBRARY_PATH後才能讓JNI找到libhello.so

完成

第三章、JNI進階一-從Java中傳參數給C程式

只能呼叫C的程式並不能滿足我們的需求,當我們要傳入參數給C的函式時,我們可以傳入Java中定義的原生型態,並且在C函式中直接使用這些原生型態,以下是原生形態的對應列表:

Java Type

Native Type

Size

boolean

jboolean

8

byte

jbyte

8

char

jchar

16

short

jshort

16

int

jint

32

long

jlong

64

float

jfloat

32

double

jdouble

64

void

void

n/a

若要使用Object作為參數的話,參數就會是jobject,JNI定義了一些方法來幫助我們轉換jobject來讓C程式使用,這裡以String和Array兩種物件為例子。

建立JAVA程式,其中傳入參數為String,其餘和第二章中相同。

public native void helloworld(String param);

javac StringHW.java //編譯JAVA程式

javah -jni StringHW //產生對應C程式的標頭檔,內容中粗體為C的函式宣告

/* DO NOT EDIT THIS FILE - it is machine generated */

#include

/* Header for class StringHW */

#ifndef _Included_StringHW

#define _Included_StringHW

#ifdef __cplusplus

extern "C" {

#endif

/*

* Class: StringHW

* Method: helloworld

* Signature: (Ljava/lang/String;)V

*/

JNIEXPORT void JNICALL Java_StringHW_helloworld

(JNIEnv *, jobject, jstring);

#ifdef __cplusplus

}

#endif

#endif

開始實作C函式的內容。這裡使用JNIEnv中的函式來處理jstring

#include "StringHW.h"

#include

JNIEXPORT void JNICALL Java_StringHW_helloworld

(JNIEnv *env, jobject obj, jstring param)

{

int str_len;

char *buf;

//get string length

str_len = (*env)->GetStringLength(env, param);

//get UTF-8 encoding char array

buf = (*env)->GetStringUTFChars(env, param, 0);

printf("UTF param:%s[%d]\n",buf,str_len);

//after using jstring object you must release it

(*env)->ReleaseStringUTFChars(env, param, buf);

//get unicode encoding char array

buf = (*env)->GetStringChars(env, param, 0);

printf("Char param:%s[%d]\n",buf,str_len);

//release string

(*env)->ReleaseStringChars(env, param, buf);

}

其餘同第二章的步驟。

第四章、JNI進階二-由C函式中存取Java中的變數

要達到Java和C程式之間的互動,C程式必需也可以去修改Java物件中的成員變數。JNIEnv提供了一組函式讓C程式可以順利地取存Java中的變數。

Java定義的成員變數中,每個都有signature(簽章)。在native code中要存取成員變數時,必須要先得知變數的簽章。以下指令是用來取得變數簽章:javap -s -p classname。該指令會列出所有成員變數的簽章,其內容大約如下:

Compiled from "TMC_GUIWarrant.java"

public class TMC_GUI.TMC_GUIWarrant extends javax.swing.JFrame{

java.lang.String ExpDate_str;

Signature: Ljava/lang/String;

java.lang.String UpperWarrantBin_str;

Signature: Ljava/lang/String;

java.lang.String OriSignerCertPath_str;

Signature: Ljava/lang/String;

java.lang.String ProxySignerCertPath_str;

Signature: Ljava/lang/String;

.

.

.

static javax.swing.JLabel access$000(TMC_GUI.TMC_GUIWarrant);

Signature: (LTMC_GUI/TMC_GUIWarrant;)Ljavax/swing/JLabel;

static javax.swing.JLabel access$100(TMC_GUI.TMC_GUIWarrant);

Signature: (LTMC_GUI/TMC_GUIWarrant;)Ljavax/swing/JLabel;

static javax.swing.JLabel access$200(TMC_GUI.TMC_GUIWarrant);

Signature: (LTMC_GUI/TMC_GUIWarrant;)Ljavax/swing/JLabel;

static javax.swing.JLabel access$300(TMC_GUI.TMC_GUIWarrant);

Signature: (LTMC_GUI/TMC_GUIWarrant;)Ljavax/swing/JLabel;

static {};

Signature: ()V

}

第五章、FAQ[常見問題解答]

Q1:在JAVA程式執行時會出現java.lang.UnsatisfiedLinkError,是什麼問題?

A1:表示在System.loadLibrary時找不到要載入的動態函式庫,請檢查LD_LIBRARY_PATH環境變數是否已經設定,或是在執行java的命令列中加入-Djava.library.path=[PathToLibrary]參數,讓JVM知道去哪裡找這些函式庫。

後來我又發現一種問題會造成java.lang.UnsatisfiedLinkError,那是libXXX.so中如果有函式名稱無法連結的話,亦會造成java無法載入該libXXX.so。但是gcc在編譯library時不會顯示這些函式名稱無法連結的錯誤,例如像warning: implicit declaration of function xxx(在動態函式庫中,找不到函式名稱是很正常的事,因為函式可能在別的library中有定義),所以強烈建議在編譯時加上-Wall的參數,gcc才會顯示這些警告。

Q2:將java的類別放到package中後,可以載入動態函式庫,但是沒辦法使用native function了 ?

A2:這是因為header改變,必需重新產生header file,修改函式庫原始碼後,再重新編譯函式庫。注意的是,產生header file的指令必須加上package name,例如:javah -jni [PACKAGE].[CLASSNAME]。

註:JNIEnv和jobject這兩個變數是無法傳到native中別的thread或process,也就是你不能在不同thread中使用env和obj這兩個變數。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值