歡迎轉載,轉載請說明。
網上很多界紹使用JAVA調用C/C++的SO庫,但從例子中大多都是一個簡單的C語言檔案,然後進行編譯打庫。
這些例子只能帶給我們瞭解打庫和調用步驟,在實際項目過程中,遇到的並非想象中的哪麼簡單。
下面我將以一個C++的例子來示範從打庫到調用的全過程,希望對初學者有用,大蝦,大牛一邊過,歡迎圍觀。
例子:
有一個汽車類介面ICar ,其中聲明了幾個基本方法,同時有一個phorsche(寶時捷)繼承ICar,並實現了相應的介面,其中用到了基礎資料型別 (Elementary Data Type)及結構體的類型,目的在於想通過該例子來實踐一下在實際項目中遇到不同類型的參數時的介面,是不是直接不需要轉換就可以調用。
interfacecar.h
#ifndef ICAR_H#define ICAR_H#include "string"typedef struct tagEngine{char name[20];char date[20];char address[100];int number;double width;double height;bool isrepair;}Engine,*pEngine;class ICar{public:ICar(void);~ICar(void);enum CarState {csStop,csRunning,csStart,csBad};virtual void setCarName(const std::string& carname)=0;virtual std::string getCarName()=0;virtual CarState getCurrentState()=0;virtual void setState(CarState cs)=0;virtual void getEngine(Engine* engine)=0;virtual Engine getEngin()=0;virtual void stop()=0;virtual void start()=0;virtual void run()=0;virtual void breakit()=0;};class Phorsche : public ICar{public: Phorsche(); ~Phorsche(); void setCarName(const std::string& carname); std::string getCarName(); CarState getCurrentState(); void setState(CarState cs); void getEngine(Engine* engine); Engine getEngin(); void stop(); void start(); void run(); void breakit();private:CarState m_state;Engine m_engine;std::string m_name;};#endif
interfacecar.cpp
#include "interfacecar.h"#include "iostream"#include "string.h"ICar::ICar(void){}ICar::~ICar(void){}void Phorsche::setCarName( const std::string& carname ){m_name = carname;}Phorsche::Phorsche(){strcpy(m_engine.address , "GZ");strcpy(m_engine.date , "2012-11-2");m_engine.height = 23.4;m_engine.width = 56.2;strcpy(m_engine.name,"Phorsche");m_engine.isrepair = false;m_engine.number = 120042912;}Phorsche::~Phorsche(){}std::string Phorsche::getCarName(){return m_name;}ICar::CarState Phorsche::getCurrentState(){return m_state;}void Phorsche::setState( CarState cs ){m_state = cs;}void Phorsche::getEngine( Engine* engine ){strcpy(engine->address , m_engine.address);strcpy(engine->date , m_engine.date);engine->height = m_engine.height;engine->width = m_engine.width;strcpy(engine->name,m_engine.name);engine->isrepair = m_engine.isrepair;engine->number = m_engine.number;}Engine Phorsche::getEngin(){return m_engine;}void Phorsche::stop(){std::cout<<"The car is stop."<<std::endl;m_state = csStop;}void Phorsche::start(){std::cout<<"The car is start."<<std::endl;m_state = csStart;}void Phorsche::run(){std::cout<<"The car is running."<<std::endl;m_state = csRunning;}void Phorsche::breakit(){std::cout<<"The car is breakit."<<std::endl;m_state = csBad;}
下面將講解打SO庫和調用過程。
準備工作:
如果是Linux環境:請按裝gcc ,g++,JDK,SWIG,[eclipse] ,中括弧的可以不裝,為了調試用的。當然也可以使用命令列來進行。
如果是WIN平台:請按裝cygwin,Swig ,JDK。
我將以LINUX和WINDOW下打SO兩種情況都進行介紹:
先對LINUX的進行介紹(小弟是LINUX菜鳥,第一次玩LINUX,也是由於打庫過程遇到XX問題後XX解決,才免強對LINUX有點點瞭解):
LINUX環境:
準備工作:
安裝:ubuntu + eclipse + openjdk1.6.3
安裝:gcc/g++ 4.6.3 版本
安裝:swig 2.0.4 版本
如果沒有裝,直接開啟終端:apt-get install <xx名>來自動按裝所需要的軟體,(反正我是這麼裝過來的)。
使用者名稱為:xxxx 不是root 許可權,這對後面的COPY檔案或刪除檔案操作上有限制。可以使用sudo su -命令進行將使用者提升為root許可權。我不知道其它LINUX行不行,反正我的ubuntu系統裝好後預設是我設定的使用者,對於ROOT我並不知道密碼,沒有辦法我只能使用sudo su -來提升。如果還有其它方法可以得到ROOT密碼的請教教小弟。
建立好檔案夾:/home/xxxx/demo 其中xxxx是我的LINUX使用者名稱;路徑大家可以自行存放了,在這個demo檔案中放置前面的interfacecar.h和interfacecar.cpp檔案。
準備工作OK。
1、先建立一個.i檔案,這個檔案相對比較重要,是由它來將C/C++介面變為JAVA調用的C介面。我這裡命名為:cplus.i
cplus.i(手寫的,不知道有沒有工具)
/* File : cplus.i */
%module cplus
%{
#include "interfacecar.h"
%}
%include "interfacecar.h"
2、使用swig 進行將.i檔案轉為JAVA檔案。
使用命令:swig -c++ -java -package com.plus -outdir ./ /home/xxxx/demo/cplus.i
說明:
-c++ :表示需要輸換的是C++介面。如果不使用這個參數,預設將以C介面進行輸換。
-java :轉為JAVA介面。
-package :指定包的名稱,當然這個不是必須的。按自己習慣。如果不使用就刪除這個參數。
-outdir : 用來指定編譯後的檔案輸出路徑。
./ : 表示輸出目錄為當前路徑。
後面的就是.i檔案存放的路徑了。
執行命令後,產生.java和xxxx_wrap.cxx檔案,
其中.java就是顯後面調用介面時尋找類型時使用的。而xxxx_wrap.cxx就是用來打SO庫的。
將產生的JAVA檔案放到測試工程demo目錄中。
3、使用CD指令進入到DEMO所在目錄路徑下。
cd /home/xxxx/demo
4、使用locate jni.h來查看jni.h所在的目錄,使用locate jni_md.h 來查看jni_md.h所在的目錄。
5、編譯cpp和cxx產生.o檔案(因為後面通過.o來產生.so)
g++ -Wall -c interfacecar.cpp cplus_wrap.cxx -I/usr/lib/jvm/java-6-openjdk-i386/include -I/usr/lib/jvm/java-6-openjdk-i386/include/linux
這裡用-I(大寫的i)來串連標頭檔所在的路徑。
另外要注意,這裡是C++所以要用G++來編,如果用GCC編,就有可能出現找不到聲明的錯誤。
將產生兩個.o檔案(interfacecar.o,cplus_wrap.o)
6、產生so庫
g++ -share -fpic interface.o cplus_wrap.o -o libcplus.so
注意:如果只是使用cplus_wrap.o一個檔案來打SO,可能出現資料類型找不到等問題。
(這個是有錯的,用於後面講解)
如果使用一個.o進行串連後面測試回合時將會報類似錯誤(我想這應該就是依賴關係未打進來導致)
上面的步驟完成
下面是ECLIPSE調用示範,JAVA高手不要見笑,第一次使用ECLIPSE。
建立一個JAVA 工程
取名為Callcplus
將前面產生的JAVA檔案添加到測試工程目錄中,如下圖。
其次將DEMO中的.Java檔案全部COPY到測試工程目錄下。
編寫測試代碼
示範調用代碼:
package com.cplus;public class Callcplus { static { System.loadLibrary("cplus"); //System.load("/home/xxxx/demo/cplus/libcplus.so"); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //System.out.print(System.getProperty("java.library.path")); //int a = example.fact(50); //double bb = example.getMy_variable(); //String bb = example.get_time(); Phorsche pche = new Phorsche(); //pche.setCarName("t m d"); //System.out.print(pche.getCarName()); pche.run(); //SWIGTYPE_p_std__string ok = "phorsche"; //pche.setCarName(ok); ICar.CarState cs = pche.getCurrentState(); Engine eg = pche.getEngin(); System.out.printf("%s\n",eg.getAddress()); System.out.printf("%s\n",eg.getDate()); System.out.printf("%s\n",eg.getName()); System.out.printf("%f\n",eg.getHeight()); System.out.printf("%f\n",eg.getWidth()); System.out.printf("%d\n",eg.getNumber()); System.out.printf("%b\n",eg.getIsrepair()); if (cs == ICar.CarState.csRunning) { System.out.print("OK"); } }}
這裡如果使用System.loadlibrary時就把lib和.so去掉,只取中間的名稱,如果使用System.load時就使用路徑如(System.load("/home/xxxx/libcplus.so"))
運行示範:
出錯了,(是因為我的eclipse找的JAVA庫路徑問題,高手自己設了,我這裡只知道將SO庫COPY到/usr/lib下,我試過放在/usr/local/lib下但不行)
於是使用cp /home/xxx/demo/libcplus.so /usr/lib
注意需要ROOT許可權
最後,再次運行。OK;
其還有一個問題未得到解決,就是我用到了std::string在C++中,但在SWIG轉類型時轉為SWIGTYPE_p_std__string了,JAVA中用的是String。因此會報String cannot to convert SWIGTYPE_p_std__string 錯誤,我查了下,好像是要在.i檔案中轉換類型。這個請大牛指教一下了。
下集,將示範在WIN平台打包SO。
好晚了,睡覺。